Search code examples
c++c++11templatessfinaeenable-if

How to get template function to use back_inserter over inserter when appropriate


How do I create a function that adds the contents of one collection to another, using std::back_inserter() if possible for efficiency? I don't see an obvious trait for push_back() and I'm not an expert with std::enable_if, but I'm hoping some combination will achieve the effect of the following:

// IF HAS_PUSH_BACK:
template<typename CIn, typename COut>
void addAll(CIn && from, COut && to) {
    std::copy(std::begin(from), std::end(from), std::back_inserter(to));
}

// IF ! HAS_PUSH_BACK:
template<typename CIn, typename COut>
void addAll(CIn && from, COut && to) {
    std::copy(std::begin(from), std::end(from), std::inserter(to, to.begin()));
}

Solution

  • How do I create a function that adds the contents of one collection to another, using back_inserter if possible for efficiency?

    I suppose you can declare a template function that return std::true_type when there is push_back()

    template <typename T>
    constexpr auto hasPushBack (int)
       -> decltype( std::declval<T>().push_back(*(std::declval<T>().begin())),
                    std::true_type() );
    

    and the fail-back function that return std::false_type

    template <typename>
    constexpr std::false_type hasPushBack (long);
    

    so you can modify your functions as follows

    template<typename CIn, typename COut>
    typename std::enable_if<true == decltype(hasPushBack<COut>(0))::value>::type
       addAll (CIn && from, COut && to)
     { std::copy(std::begin(from), std::end(from), std::back_inserter(to)); }
    
    template<typename CIn, typename COut>
    typename std::enable_if<false == decltype(hasPushBack<COut>(0))::value>::type
       addAll(CIn && from, COut && to)
     { std::copy(std::begin(from), std::end(from), std::inserter(to, to.begin())); }
    

    If you can use C++14 or newer, you can also define a template variable with the value

    template <typename T>
    constexpr bool hasPushBack_v = decltype(hasPushBack<T>(0))::value;
    

    and you can simplify the functions as follows

    template<typename CIn, typename COut>
    std::enable_if_t<true == hasPushBack_v<COut>>
       addAll (CIn && from, COut && to)
     { std::copy(std::begin(from), std::end(from), std::back_inserter(to)); }
    
    template<typename CIn, typename COut>
    std::enable_if_t<false == hasPushBack_v<COut>>
       addAll(CIn && from, COut && to)
     { std::copy(std::begin(from), std::end(from), std::inserter(to, to.begin())); }