1
votes

Comment faire en sorte que la fonction de modèle utilise back_inserter sur l'inséreuse le cas échéant

Comment créer une fonction qui ajoute le contenu d'une collection à une autre, en utilisant std :: back_inserter () si possible pour plus d'efficacité? Je ne vois pas de trait évident pour push_back () et je ne suis pas un expert avec std :: enable_if , mais j'espère qu'une combinaison produira l'effet des éléments suivants:

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


0 commentaires

3 Réponses :


3
votes

Comment créer une fonction qui ajoute le contenu d'une collection à une autre, en utilisant back_inserter si possible pour plus d'efficacité?

Je suppose que vous pouvez déclarer une fonction de modèle qui renvoie std :: true_type quand il y a push_back()

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())); }
et la fonction de restauration qui renvoie std::false_type
template <typename T>
constexpr bool hasPushBack_v = decltype(hasPushBack<T>(0))::value;

afin que vous puissiez modifier vos fonctions comme suit

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

Si vous pouvez utiliser C ++ 14 ou plus récent, vous pouvez également définir une variable de modèle avec la valeur

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

et vous pouvez simplifier le fonctionne comme suit

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

0 commentaires

1
votes

Vous pouvez appliquer SFINAE à l'aide de std :: enable_if et 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 commentaires

2
votes

Juste pour le plaisir, à partir de C ++ 14, vous pouvez également utiliser le modèle de variable

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


2 commentaires

Gentil et drôle. Malheureusement, std :: void_t est C ++ 17 (mais il est facile de le remplacer dans C ++ 14)


@ max66 - vous avez raison. Je l'ai ajouté par souci d'exhaustivité.