2016-11-20 2 views
2

J'ai une classe personnalisée qui a une interface de type tuple. Parce que je veux que mon code soit aussi générique que possible, j'ai pensé que ce serait une bonne idée de baser mes algorithmes sur les fonctions std::get, std::tuple_size, std::tuple_element alors vous devez juste spécialiser ces fonctions pour utiliser mes algorithmes. Appelons le concept qui nécessite ces spécialisations de fonction Tuple.Somme les composants d'un tuple en utilisant std :: get, std :: tuple_size, std :: tuple_element

Maintenant j'essaie de résumer les composants d'un Tuple. La déclaration de fonction doit être quelque chose comme ceci:

template <class Tuple> 
int sum_components(const Tuple& t); 

Je suppose qu'il ya beaucoup de modèle de programmation impliqué, mais je ne peux pas comprendre comment le faire. Pour l'addition je voudrais simplement utiliser une surcharge de la + operator globale.

J'utilise C++ 1z.

Répondre

8

Ceci est très facile dans .

template<class Tuple> 
decltype(auto) sum_components(Tuple const& tuple) { 
    auto sum_them = [](auto const&... e)->decltype(auto) { 
    return (e+...); 
    }; 
    return std::apply(sum_them, tuple); 
}; 

ou (...+e) pour la direction de pliage opposée.

Dans les versions précédentes, la bonne approche consistait à écrire le vôtre apply plutôt que d'écrire une implémentation sur mesure. Lorsque votre compilateur est mis à jour, vous pouvez ensuite supprimer le code.

En , je pourrais le faire:

// namespace for utility code: 
namespace utility { 
    template<std::size_t...Is> 
    auto index_over(std::index_sequence<Is...>) { 
    return [](auto&&f)->decltype(auto){ 
     return decltype(f)(f)(std::integral_constant<std::size_t,Is>{}...); 
    }; 
    } 
    template<std::size_t N> 
    auto index_upto() { 
    return index_over(std::make_index_sequence<N>{}); 
    } 
} 
// namespace for semantic-equivalent replacements of `std` code: 
namespace notstd { 
    template<class F, class Tuple> 
    decltype(auto) apply(F&& f, Tuple&& tuple) { 
    using dTuple = std::decay_t<Tuple>; 
    auto index = ::utility::index_upto< std::tuple_size<dTuple>{} >(); 
    return index([&](auto...Is)->decltype(auto){ 
     auto target=std::ref(f); 
     return target(std::get<Is>(std::forward<Tuple>(tuple))...); 
    }); 
    } 
} 

qui est assez proche de std::apply dans . (J'abuse std::ref pour obtenir INVOKE sémantique). (Cela ne fonctionne pas parfaitement avec les invocateurs de rvalue, mais c'est très le cas).

En , je vous conseille de mettre à jour votre compilateur à ce stade. Dans je vous conseille de mettre à jour votre travail à ce stade.


Tout ce qui précède fait des plis droit ou gauche. Dans certains cas, un pli d'arbre binaire pourrait être meilleur. C'est plus compliqué.

Si votre + exécute des modèles d'expression, le code ci-dessus ne fonctionnera pas correctement en raison de problèmes de durée de vie. Vous devrez peut-être ajouter un autre type de modèle pour «après, cast-to» pour que l'arbre d'expression temporaire puisse être évalué dans certains cas.

+0

Juste besoin de saupoudrer 'decltype (auto)'. –

+0

@ T.C. Ajouté au goût. Omis à l'origine parce que je ne voulais pas penser à des modèles d'expression et d'autres dégâts. Il y a une faible chance que les résultats intermédiaires retournés comme 'T && 'à partir d'un champ d'un objet de stockage automatique temporaire puissent rompre avec ce qui précède. Mais maintenant je pense que c'est le problème du modèle d'expression. – Yakk

+0

Merci beaucoup. Cette solution est encore meilleure que celle de @ krzaq parce que vous utilisez 'std :: apply' que je ne connaissais pas auparavant. J'ai accepté votre solution à la place. – Max

4

Avec C++ 1z c'est assez simple avec fold expressions. Tout d'abord, transférer le nuplet à une fonction _impl et lui fournit séquence d'index pour accéder à tous les éléments de tuple, puis somme:

template<typename T, size_t... Is> 
auto sum_components_impl(T const& t, std::index_sequence<Is...>) 
{ 
    return (std::get<Is>(t) + ...); 
} 

template <class Tuple> 
int sum_components(const Tuple& t) 
{ 
    constexpr auto size = std::tuple_size<Tuple>{}; 
    return sum_components_impl(t, std::make_index_sequence<size>{}); 
} 

demo


Une approche 14 C++ serait de somme récursive un pack variadique:

int sum() 
{ 
    return 0; 
} 

template<typename T, typename... Us> 
auto sum(T&& t, Us&&... us) 
{ 
    return std::forward<T>(t) + sum(std::forward<Us>(us)...); 
} 

template<typename T, size_t... Is> 
auto sum_components_impl(T const& t, std::index_sequence<Is...>) 
{ 
    return sum(std::get<Is>(t)...); 
} 

template <class Tuple> 
int sum_components(const Tuple& t) 
{ 
    constexpr auto size = std::tuple_size<Tuple>{}; 
    return sum_components_impl(t, std::make_index_sequence<size>{}); 
} 

demo

Une approche C++ 11 serait l'approche C++ 14 avec une implémentation personnalisée de index_sequence. Par exemple de here.


Comme @ildjarn a souligné dans les commentaires, les exemples ci-dessus sont tous les deux emploient des plis à droite, tandis que de nombreux programmeurs attendent plis gauche dans leur code. La version C++ 1Z est trivialement variable:

template<typename T, size_t... Is> 
auto sum_components_impl(T const& t, std::index_sequence<Is...>) 
{ 
    return (... + std::get<Is>(t)); 
} 

demo

Et le 14 C++ est pas bien pire, mais il y a plus de changements:

template<typename T, typename... Us> 
auto sum(T&& t, Us&&... us) 
{ 
    return sum(std::forward<Us>(us)...) + std::forward<T>(t); 
} 

template<typename T, size_t... Is> 
auto sum_components_impl(T const& t, std::index_sequence<Is...>) 
{ 
    constexpr auto last_index = sizeof...(Is) - 1; 
    return sum(std::get<last_index - Is>(t)...); 
} 

demo

+1

Intéressant que vous utilisiez un pli gauche pour C++ 14 mais un pli droit pour C++ 17. ; -] – ildjarn

+0

@ildjarn Bon point. Je ne l'ai pas fait consciemment, mais je pense que c'était parce que je voulais instinctivement garder l'élément «connu» du côté gauche. Mais si son op + n'est pas associatif ou commutatif alors OP a un autre sac de problèmes;) – krzaq

+0

D'accord, _ ne devrait pas avoir d'importance pour 'sum' (bien que' + 'ne soit pas nécessairement sûr - 'std :: string'), mais en général je pense que les gens qui sont exposés pour la première fois à des expressions de fold devraient être montrés à gauche, car c'est ce que la plupart des programmeurs C++ vont attendre (n'importe qui avec un fond FP ne sera pas dupé une fois sont possibles). Pas une critique de votre réponse, juste une observation. :-D – ildjarn