2015-10-02 1 views
3

Compte tenune peut expliquer la spécialisation du modèle ambigu

template <typename...> struct Pack; 
using T1 = std::tuple<int, char, double>; 
using T2 = std::tuple<bool, double, int, char>; 

TupleTree<Pack, T1, T2> sont

Pack< 
    Pack<int, bool>, Pack<int, double>, Pack<int, int>, Pack<int, char>, 
    Pack<char, bool>, Pack<char, double>, Pack<char, int>, Pack<char, char>, 
    Pack<double, bool>, Pack<double, double>, Pack<double, int>, Pack<double, char> 
> 

Et cela s'étend à un certain nombre de tuples. Assez facile à comprendre la définition? D'accord, j'ai fonctionner correctement le programme:

http://ideone.com/OumeZK

Mais maintenant, je veux étendre la définition à

`TupleTreeWithRepeats<P, std::index_sequence<Is...>, Tuples...>` 

Is... indique le nombre de fois que chaque tuple est utilisé de façon répétée jusqu'à ce que le déplacement sur le prochain tuple. Notez que <Is...> = <1,1,...,1> réduira au même que TupleTree<P, Tuples...>. L'ambiguïté que je suis coincé avec est avec ces deux spécialisations:

TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber, P<Ts...>, First, Rest...> 
TupleTreeWithRepeatsHelper<P, std::index_sequence<I, Is...>, I, P<Ts...>, First, Rest...> 

Pour une raison quelconque, la présence de P<Ts...> provoque l'ambiguïté parce que quand je le remplace par un seul type nommé l'ambiguïté est supprimée. Même lorsque je remplace std::index_sequence<Is...> par std::index_sequence<I, Is...>, l'ambiguïté est toujours là. Que se passe-t-il? Et comment résoudre ce problème? Voici le code, ce qui est presque le même que celui pour TupleTree:

#include <iostream> 
#include <tuple> 
#include <type_traits> 

template <typename T> struct Identity { using type = T; }; 

// Merging packs of types. 
template <typename...> struct MergePacks; 

template <typename Pack> 
struct MergePacks<Pack> : Identity<Pack> {}; 

template <template <typename...> class P, typename... Types1, typename... Types2, typename... Packs> 
struct MergePacks<P<Types1...>, P<Types2...>, Packs...> : MergePacks<P<Types1..., Types2...>, Packs...> {}; 

// Appending a type to a pack. 
template <typename Pack, typename T> struct AppendType; 

template <template <typename...> class P, typename... Ts, typename T> 
struct AppendType <P<Ts...>, T> { 
    using type = P<Ts..., T>; 
}; 

// ExpandPackWithTuple takes a pack, and creates N packs that each end with the tuple's elements, N is the size of the tuple. 
template <template <typename...> class P, typename Pack, typename Tuple, typename Indices> struct ExpandPackWithTupleHelper; 

template <template <typename...> class P, typename Pack, typename Tuple, std::size_t... Is> 
struct ExpandPackWithTupleHelper<P, Pack, Tuple, std::index_sequence<Is...>> { 
    using type = P<typename AppendType<Pack, typename std::tuple_element<Is, Tuple>::type>::type...>; 
}; 

template <template <typename...> class P, typename Pack, typename Tuple> 
using ExpandPackWithTuple = typename ExpandPackWithTupleHelper<P, Pack, Tuple, std::make_index_sequence<std::tuple_size<Tuple>::value>>::type; 

// TupleTreeWithRepeats. 
template <template <typename...> class P, typename NumRepeats, std::size_t LoopNumber, typename OutputPack, typename... Tuples> struct TupleTreeWithRepeatsHelper; 

template <template <typename...> class P, std::size_t I, std::size_t... Is, std::size_t LoopNumber, typename... Ts, typename First, typename... Rest> 
struct TupleTreeWithRepeatsHelper<P, std::index_sequence<I, Is...>, LoopNumber, P<Ts...>, First, Rest...> : 
    TupleTreeWithRepeatsHelper<P, std::index_sequence<I, Is...>, LoopNumber + 1, typename MergePacks<ExpandPackWithTuple<P, Ts, First>...>::type, First, Rest...> {}; 

template <template <typename...> class P, std::size_t I, std::size_t... Is, typename... Ts, typename First, typename... Rest> 
struct TupleTreeWithRepeatsHelper<P, std::index_sequence<I, Is...>, I, P<Ts...>, First, Rest...> : 
    TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, 0, typename MergePacks<ExpandPackWithTuple<P, Ts, First>...>::type, Rest...> {}; 

template <template <typename...> class P, std::size_t... Is, std::size_t LoopNumber, typename OutputPack> 
struct TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber, OutputPack> { 
    using type = OutputPack; 
}; 

template <template <typename...> class P, typename NumRepeats, typename... Tuples> struct TupleTreeWithRepeats; 

template <template <typename...> class P, std::size_t I, std::size_t... Is, typename... Tuples> 
struct TupleTreeWithRepeats<P, std::index_sequence<I, Is...>, Tuples...> : TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, 0, P<P<>>, Tuples...> {}; 

// Testing 
template <typename...> struct Pack; 
using T1 = std::tuple<int, char, double>; 
using T2 = std::tuple<bool, double, int, char>; 
using T3 = std::tuple<double, int>; 

int main() { 
    std::cout << std::is_same< 
     TupleTreeWithRepeats<Pack, std::index_sequence<1,1,1>, T1, T2, T3>::type, 
     Pack< 
      Pack<int, bool, double>, Pack<int, bool, int>, Pack<int, double, double>, Pack<int, double, int>, Pack<int, int, double>, Pack<int, int, int>, Pack<int, char, double>, Pack<int, char, int>, 
      Pack<char, bool, double>, Pack<char, bool, int>, Pack<char, double, double>, Pack<char, double, int>, Pack<char, int, double>, Pack<char, int, int>, Pack<char, char, double>, Pack<char, char, int>, 
      Pack<double, bool, double>, Pack<double, bool, int>, Pack<double, double, double>, Pack<double, double, int>, Pack<double, int, double>, Pack<double, int, int>, Pack<double, char, double>, Pack<double, char, int> 
     > 
    >::value << '\n'; // ambiguous 
} 
+0

Fait intéressant, il est pas ambigu pour clang: http: //coliru.stacked -crooked.com/a/4272e7d68f252d6e –

+0

Je compile avec GCC 5.1. En passant, j'ai légèrement modifié mon code, car j'ai repéré une erreur de logique dans la deuxième spécialisation. Mais l'ambiguïté est toujours le principal problème.Mais l'ambiguïté doit d'abord être supprimée avant d'affiner les spécialisations et de tester la sortie. – prestokeys

+0

@MarcoA. Je pense que la compilation a simplement dépassé ce délai. En regardant la sortie avec moins de profondeur, on dirait que clang descendait une récursion infinie. –

Répondre

1

Pour autant que je peux dire, ces deux ne devrait pas être ambiguë.

Quoiqu'il en soit, une solution simple consiste à mettre en œuvre juste "avec des répétitions" en termes de "sans répétitions":

// TupleTreeWithRepeats. 
template <template <typename...> class P, typename Tuples> 
struct TupleTreeWithRepeatsHelper; 

template <template <typename...> class P, typename... Tuples> 
struct TupleTreeWithRepeatsHelper<P, Pack<Tuples...>> : 
    Identity<TupleTree<P, Tuples...>> {}; 

template <template <typename...> class P, typename NumRepeats, typename... Tuples> 
struct TupleTreeWithRepeats; 

template <template <typename...> class P, std::size_t... Is, typename... Tuples> 
struct TupleTreeWithRepeats<P, std::index_sequence<Is...>, Tuples...> : 
    TupleTreeWithRepeatsHelper<P, typename MergePacks<repeat<Tuples, Is>...>::type> {}; 

repeat est

template<class T, std::size_t> using id = T; 

template<class T, std::size_t... Is> 
Pack<id<T, Is>...> do_repeat(std::index_sequence<Is...>); 

template<class T, std::size_t I> 
using repeat = decltype(do_repeat<T>(std::make_index_sequence<I>())); 

Demo. On pourrait dire que c'est aussi un meilleur design.


minimisés à:

template<class> class Purr { }; 

template <template <class> class, class> 
struct Meow; 

template <template <class> class P> 
struct Meow<P, P<int>> { }; 

template <template <class> class P, class T> 
struct Meow<P, P<T>> { }; 

Meow<Purr, Purr<int>> c; 

GCC signale une ambiguïté, qui ressemble vraiment à un bug pour moi. Clang gère cela correctement.

+0

Merci pour la solution de contournement, T.C. Mais puisque la compréhension du langage C++ est plus importante que mon programme, la première implémentation est-elle vraiment ambiguë ou non? Je sais que vous avez exprimé votre opinion. Mais si c'est vraiment ambigu, une solution de contournement comme votre solution ci-dessus existe-t-elle toujours? – prestokeys

0

Bien que la solution de contournement de T.C. soit certainement plus élégante, cette solution de contournement que j'ai trouvée pourrait être la solution de contournement générale qui devrait toujours fonctionner à chaque fois que ce bogue GCC est rencontré. Au lieu d'éviter le bug du GCC, je l'ai confronté. Il suffit de remplacer P<Ts...> avec un seul typename et définir une méta-fonction distincte qui l'utilise:

template <template <typename...> class P, typename OutputPack, typename Tuple> struct AddToOutput; 

template <template <typename...> class P, typename... Ts, typename Tuple> 
struct AddToOutput<P, P<Ts...>, Tuple> : MergePacks<ExpandPackWithTuple<P, Ts, Tuple>...> {}; 

// My workaround, using struct AddToOutput. 
template <template <typename...> class P, std::size_t... Is, std::size_t LoopNumber, typename OutputPack, typename First, typename... Rest> 
struct TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber, OutputPack, First, Rest...> : 
    TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber + 1, typename AddToOutput<P, OutputPack, First>::type, First, Rest...> {}; 

template <template <typename...> class P, std::size_t I, std::size_t... Is, typename OutputPack, typename First, typename... Rest> 
struct TupleTreeWithRepeatsHelper<P, std::index_sequence<I, Is...>, I, OutputPack, First, Rest...> : 
    TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, 1, typename AddToOutput<P, OutputPack, First>::type, Rest...> {}; 

Le code entier:

#include <iostream> 
#include <tuple> 

template <typename T> struct Identity { using type = T; }; 

// Merging packs of types. 
template <typename...> struct MergePacks; 

template <typename Pack> 
struct MergePacks<Pack> : Identity<Pack> {}; 

template <template <typename...> class P, typename... Types1, typename... Types2, typename... Packs> 
struct MergePacks<P<Types1...>, P<Types2...>, Packs...> : MergePacks<P<Types1..., Types2...>, Packs...> {}; 

// Appending a type to a pack. 
template <typename Pack, typename T> struct AppendType; 

template <template <typename...> class P, typename... Ts, typename T> 
struct AppendType <P<Ts...>, T> { 
    using type = P<Ts..., T>; 
}; 

// ExpandPackWithTuple takes a pack, and creates N packs that each end with the tuple's elements, where N is the size of the tuple. 
template <template <typename...> class P, typename Pack, typename Tuple, typename Indices> struct ExpandPackWithTupleHelper; 

template <template <typename...> class P, typename Pack, typename Tuple, std::size_t... Is> 
struct ExpandPackWithTupleHelper<P, Pack, Tuple, std::index_sequence<Is...>> { 
    using type = P<typename AppendType<Pack, typename std::tuple_element<Is, Tuple>::type>::type...>; 
}; 

template <template <typename...> class P, typename Pack, typename Tuple> 
using ExpandPackWithTuple = typename ExpandPackWithTupleHelper<P, Pack, Tuple, std::make_index_sequence<std::tuple_size<Tuple>::value>>::type; 

// Now, for TupleTree itself. 
template <template <typename...> class P, typename OutputPack, typename... Tuples> struct TupleTreeHelper; 

template <template <typename...> class P, typename... Ts, typename First, typename... Rest> 
struct TupleTreeHelper<P, P<Ts...>, First, Rest...> : TupleTreeHelper<P, typename MergePacks<ExpandPackWithTuple<P, Ts, First>...>::type, Rest...> {}; 

template <template <typename...> class P, typename OutputPack> 
struct TupleTreeHelper<P, OutputPack> { 
    using type = OutputPack; 
}; 

template <template <typename...> class P, typename... Tuples> 
using TupleTree = typename TupleTreeHelper<P, P<P<>>, Tuples...>::type; 

// TupleTreeWithRepeats. 
template <template <typename...> class P, typename NumRepeats, std::size_t LoopNumber, typename OutputPack, typename... Tuples> struct TupleTreeWithRepeatsHelper; 

template <template <typename...> class P, typename OutputPack, typename Tuple> struct AddToOutput; // This is only needed for a workaround against the GCC 5.1 bug explained below. 

template <template <typename...> class P, typename... Ts, typename Tuple> 
struct AddToOutput<P, P<Ts...>, Tuple> : MergePacks<ExpandPackWithTuple<P, Ts, Tuple>...> {}; 

// The following two specializations run into ambiguity with GCC 5.1, though Clang runs it fine. It is GCC that is bugged. 
//template <template <typename...> class P, std::size_t... Is, std::size_t LoopNumber, typename... Ts, typename First, typename... Rest> 
//struct TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber, P<Ts...>, First, Rest...> : 
// TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber + 1, typename MergePacks<ExpandPackWithTuple<P, Ts, First>...>::type, First, Rest...> {}; 
// 
//template <template <typename...> class P, std::size_t I, std::size_t... Is, typename... Ts, typename First, typename... Rest> 
//struct TupleTreeWithRepeatsHelper<P, std::index_sequence<I, Is...>, I, P<Ts...>, First, Rest...> : 
// TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, 1, typename MergePacks<ExpandPackWithTuple<P, Ts, First>...>::type, Rest...> {}; 

// My workaround, using struct AddToOutput. 
template <template <typename...> class P, std::size_t... Is, std::size_t LoopNumber, typename OutputPack, typename First, typename... Rest> 
struct TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber, OutputPack, First, Rest...> : 
    TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber + 1, typename AddToOutput<P, OutputPack, First>::type, First, Rest...> {}; 

template <template <typename...> class P, std::size_t I, std::size_t... Is, typename OutputPack, typename First, typename... Rest> 
struct TupleTreeWithRepeatsHelper<P, std::index_sequence<I, Is...>, I, OutputPack, First, Rest...> : 
    TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, 1, typename AddToOutput<P, OutputPack, First>::type, Rest...> {}; 

template <template <typename...> class P, typename OutputPack> 
struct TupleTreeWithRepeatsHelper<P, std::index_sequence<>, 1, OutputPack> { 
    using type = OutputPack; 
}; 

template <template <typename...> class P, typename NumRepeats, typename... Tuples> struct TupleTreeWithRepeats; 

template <template <typename...> class P, std::size_t... Is, typename... Tuples> 
struct TupleTreeWithRepeats<P, std::index_sequence<Is...>, Tuples...> : TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, 1, P<P<>>, Tuples...> {}; 

// Testing 
#include <type_traits> 

template <typename...> struct Pack; 
using T1 = std::tuple<int, char, double>; 
using T2 = std::tuple<bool, double, int, char>; 
using T3 = std::tuple<double, int>; 

int main() { 
    std::cout << std::boolalpha; 
    std::cout << std::is_same< 
     ExpandPackWithTuple<Pack, Pack<int, bool>, std::tuple<int, char, double>>, 
     Pack<Pack<int, bool, int>, Pack<int, bool, char>, Pack<int, bool, double>> 
    >::value << '\n'; // true 

    std::cout << std::is_same< 
     MergePacks<ExpandPackWithTuple<Pack, Pack<>, T1>>::type, 
     Pack<Pack<int>, Pack<char>, Pack<double>> 
    >::value << '\n'; // true 

    std::cout << std::is_same< 
     MergePacks<ExpandPackWithTuple<Pack, Pack<int>, T2>, ExpandPackWithTuple<Pack, Pack<char>, T2>, ExpandPackWithTuple<Pack, Pack<double>, T2>>::type, 
     Pack< 
      Pack<int, bool>, Pack<int, double>, Pack<int, int>, Pack<int, char>, 
      Pack<char, bool>, Pack<char, double>, Pack<char, int>, Pack<char, char>, 
      Pack<double, bool>, Pack<double, double>, Pack<double, int>, Pack<double, char> 
     > 
    >::value << '\n'; // true 

    std::cout << std::is_same< 
     TupleTree<Pack, T1, T2>, 
     Pack< 
      Pack<int, bool>, Pack<int, double>, Pack<int, int>, Pack<int, char>, 
      Pack<char, bool>, Pack<char, double>, Pack<char, int>, Pack<char, char>, 
      Pack<double, bool>, Pack<double, double>, Pack<double, int>, Pack<double, char> 
     > 
    >::value << '\n'; // true 

    std::cout << std::is_same< 
     TupleTree<Pack, T1, T2, T3>, 
     Pack< 
      Pack<int, bool, double>, Pack<int, bool, int>, Pack<int, double, double>, Pack<int, double, int>, Pack<int, int, double>, Pack<int, int, int>, Pack<int, char, double>, Pack<int, char, int>, 
      Pack<char, bool, double>, Pack<char, bool, int>, Pack<char, double, double>, Pack<char, double, int>, Pack<char, int, double>, Pack<char, int, int>, Pack<char, char, double>, Pack<char, char, int>, 
      Pack<double, bool, double>, Pack<double, bool, int>, Pack<double, double, double>, Pack<double, double, int>, Pack<double, int, double>, Pack<double, int, int>, Pack<double, char, double>, Pack<double, char, int> 
     > 
    >::value << '\n'; // true 

    std::cout << std::is_same< 
     TupleTreeWithRepeats<Pack, std::index_sequence<1,1>, T1, T2>::type, 
     Pack< 
      Pack<int, bool>, Pack<int, double>, Pack<int, int>, Pack<int, char>, 
      Pack<char, bool>, Pack<char, double>, Pack<char, int>, Pack<char, char>, 
      Pack<double, bool>, Pack<double, double>, Pack<double, int>, Pack<double, char> 
     > 
    >::value << '\n'; // true 

    std::cout << std::is_same< 
     TupleTreeWithRepeats<Pack, std::index_sequence<1,1,1>, T1, T2, T3>::type, 
     Pack< 
      Pack<int, bool, double>, Pack<int, bool, int>, Pack<int, double, double>, Pack<int, double, int>, Pack<int, int, double>, Pack<int, int, int>, Pack<int, char, double>, Pack<int, char, int>, 
      Pack<char, bool, double>, Pack<char, bool, int>, Pack<char, double, double>, Pack<char, double, int>, Pack<char, int, double>, Pack<char, int, int>, Pack<char, char, double>, Pack<char, char, int>, 
      Pack<double, bool, double>, Pack<double, bool, int>, Pack<double, double, double>, Pack<double, double, int>, Pack<double, int, double>, Pack<double, int, int>, Pack<double, char, double>, Pack<double, char, int> 
     > 
    >::value << '\n'; // true 
}