vendredi 2 octobre 2015

Cannot explain ambiguous template specializations

Given

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

TupleTree<Pack, T1, T2> shall be

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>
>

And this extends to any number of tuples. Easy enough to understand the definition? Ok, I have the program working correctly:

http://ift.tt/1N9Rolb

But now I want to extend the definition to

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

where Is... will indicate the number of times each tuple is being used repeatedly until moving on to the next tuple. <Is...> = <1,1,...,1> will reduce to the same as TupleTree<P, Tuples...>. The ambiguity that I'm stuck with is with these two specializations:

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

For some reason, the presence of P<Ts...> causes the ambiguity because when I replace it with a single-named type the ambiguity is removed. What's going on there? Here is the code, which is almost the same as that for 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, std::size_t NumLoops, typename OutputPack, typename... Tuples> struct TupleTreeWithRepeatsHelper;

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

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

template <template <typename...> class P, std::size_t... Is, std::size_t LoopNumber, std::size_t NumLoops, typename OutputPack>
struct TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber, NumLoops, 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, I, 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
}

Aucun commentaire:

Enregistrer un commentaire