Как получить функцию шаблона для использования back_inserter над вставкой при необходимости - PullRequest
0 голосов
/ 01 января 2019

Как создать функцию, которая добавляет содержимое одной коллекции в другую, используя std::back_inserter(), если это возможно, для эффективности?Я не вижу очевидной черты для push_back(), и я не эксперт по std::enable_if, но я надеюсь, что некоторая комбинация достигнет эффекта следующего:

// 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()));
}

Ответы [ 3 ]

0 голосов
/ 01 января 2019

Вы можете подать SFINAE с помощью std::enable_if и std::void_t.

template <typename T, typename = void>
struct has_push_back : std::false_type {};
template <typename T>
struct has_push_back<T, std::void_t<decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))>>
    : std::true_type {};

// IF HAS_PUSH_BACK:
template<typename CIn, typename COut>
std::enable_if_t<has_push_back<std::remove_reference_t<COut>>::value> 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>
std::enable_if_t<!has_push_back<std::remove_reference_t<COut>>::value> addAll(CIn && from, COut && to) {
    std::copy(std::begin(from), std::end(from), std::inserter(to, to.begin()));
}

LIVE

0 голосов
/ 01 января 2019

Ради интереса, начиная с C ++ 14, вы также можете использовать переменную template

template <class...> using void_t = void; // (compensate C++14 lack)

template <class T, class = void>
constexpr bool HasPushBack{false};

template <class T>
constexpr bool HasPushBack<T, void_t<
    decltype(std::declval<T>().push_back(std::declval<typename std::decay_t<T>::value_type>()))>
>{true};

template<typename CIn, typename COut, std::enable_if_t< HasPushBack<COut>,bool> = true>
void 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<!HasPushBack<COut>,bool> = true>
void addAll(CIn && from, COut && to) {
    std::copy(std::begin(from), std::end(from), std::inserter(to, to.begin()));
}
0 голосов
/ 01 января 2019

Как создать функцию, которая добавляет содержимое одной коллекции в другую, используя, если возможно, back_inserter для эффективности?

Я полагаю, вы можете объявить функцию шаблона, которая возвращает std::true_type когда есть push_back()

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

и функция возврата при сбое, которая возвращает std::false_type

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

, чтобы вы могли изменить свои функции следующим образом

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())); }

Если вы можете использовать C ++ 14 или новее, вы также можете определить переменную шаблона со значением

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

, и вы можете упростить функции следующим образом:

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())); }
...