J'essaye de pratiquer une programmation de modèle. Il existe peut-être un moyen standard de le faire, et je serais reconnaissant pour ces réponses, mais mon objectif principal est de pratiquer les techniques de programmation de modèles, alors j'ai essayé de l'implémenter moi-même:

J'ai besoin de concaténer plusieurs tuples, mais en tant que types, pas comme std::cat_tuple le fait. J'ai donc besoin de quelque chose comme cat<std::tuple<int, float>, std::tuple<char, bool>, ...> pour obtenir std::tuple<int, float, char, bool, ...> comme type.

Ma tentative actuelle a échoué avec une erreur is not a template:

/* Concat tuples as types: */
template <typename first_t, typename... rest_t> struct cat {
    using type = typename _cat<first_t, typename cat<rest_t...>::type>::type;
                          ^^^^ cat is not a template
};
template <typename first_t, typename second_t>
struct cat<first_t, second_t> {
    using type = typename _cat<first_t, second_t>::type;
                          ^^^^ cat is not a template
};
// Concat two tuples:
template <typename, typename> struct _cat;
template <typename tuple_t, typename first_t, typename... rest_t>
struct _cat<tuple_t, std::tuple<first_t, rest_t...>> {
    using type = typename _cat<typename append<first_t, tuple_t>::type, std::tuple<rest_t...>>::type;
};
template <typename tuple_t, typename first_t>
struct _cat<tuple_t, std::tuple<first_t>> {
    using type = typename append<first_t, tuple_t>::type;
};
// Prepend element to tuple:
template <typename, typename> struct prepend;
template <typename elem_t, typename... tuple_elem_t>
struct prepend<elem_t, std::tuple<tuple_elem_t...>> {
    using type = std::tuple<elem_t, tuple_elem_t...>;
};
// Apppend element to tuple:
template <typename, typename> struct append;
template <typename elem_t, typename... tuple_elem_t>
struct append<elem_t, std::tuple<tuple_elem_t...>> {
    using type = std::tuple<tuple_elem_t..., elem_t>;
};

Quelle peut être la cause de l'erreur?

Est-ce une bonne approche? Cela pourrait être résolu de manière plus simple, mais je voulais que ce soit polyvalent (avec les opérations d'ajout / d'avance, etc.).

8
egst 20 nov. 2018 à 16:29

3 réponses

Meilleure réponse

Après réorganiser un peu la définition, votre code fonctionne correctement.

Je ne pense pas qu'il existe des lignes directrices pour la programmation de méta-modèles. Probablement à cause du fait que le comité C ++ améliore "agressivement" TMP et que trop peu de gens utilisent TMP.

Voici ma version de Cat, elle suit fondamentalement la même structure que la vôtre:

template <class, class>
struct Cat;
template <class... First, class... Second>
struct Cat<std::tuple<First...>, std::tuple<Second...>> {
    using type = std::tuple<First..., Second...>;
};
4
felix 20 nov. 2018 à 13:45

Que diriez-vous d'un alias de modèle direct à une ligne:

template<typename ... input_t>
using tuple_cat_t=
decltype(std::tuple_cat(
    std::declval<input_t>()...
));


tuple_cat_t<
    std::tuple<int,float>,
    std::tuple<int>
    > test{1,1.0f,2};
4
Red.Wave 20 nov. 2018 à 17:55

Est-ce trop tard pour jouer?

Je propose la solution suivante

template <typename T, typename ...>
struct cat
 { using type = T; };

template <template <typename ...> class C,
          typename ... Ts1, typename ... Ts2, typename ... Ts3>
struct cat<C<Ts1...>, C<Ts2...>, Ts3...>
   : public cat<C<Ts1..., Ts2...>, Ts3...>
 { };

Observez que cette solution ne fonctionne pas seulement avec une liste variadique de std::tuple mais aussi avec un conteneur générique de types. Si une solution std::tuple - seule vous suffit, vous pouvez la simplifier comme suit

template <typename T, typename ...>
struct cat
 { using type = T; };

template <typename ... Ts1, typename ... Ts2, typename ... Ts3>
struct cat<std::tuple<Ts1...>, std::tuple<Ts2...>, Ts3...>
   : public cat<std::tuple<Ts1..., Ts2...>, Ts3...>
 { };

Vous pouvez tester qui fonctionne avec

   using t1 = typename cat<std::tuple<int, float>,
                           std::tuple<char, bool>,
                           std::tuple<long, char, double>>::type;

   using t2 = std::tuple<int, float, char, bool, long, char, double>;

   static_assert(std::is_same<t1, t2>::value, "!");

- MODIFIER -

Comme indiqué par felix (merci!) Avec ma solution précédente, nous avons cela

std::is_same<int, typename cat<int>::type>::value == true

C'est-à-dire que ... cat<T>::type est également défini lorsque T n'est pas un std::tuple.

C'est un problème?

Je ne sais pas car je ne sais pas comment est utilisé cat<T>::type.

Quoi qu'il en soit ... évitez d'imposer que cat<Ts...>::type ne soit défini que lorsque tous les Ts... sont des conteneurs de type (avec le même conteneur), c'est simple: la version principale de cat devient seulement déclarée mais pas défini

template <typename, typename ...> // or also template <typename...>
struct cat;

Et est introduit une spécialisation supplémentaire avec un seul type (mais uniquement lorsqu'il s'agit d'un conteneur de type).

template <template <typename ...> class C, typename ... Ts1>
struct cat<C<Ts1...>>
 { using type = C<Ts1...>; };
4
max66 20 nov. 2018 à 14:48