无法解释不明确的模板专业化
Cannot explain ambiguous template specializations
给定
template <typename...> struct Pack;
using T1 = std::tuple<int, char, double>;
using T2 = std::tuple<bool, double, int, char>;
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>
>
这扩展到任意数量的元组。定义足够容易理解吗?好的,我的程序运行正常:
但现在我想将定义扩展到
`TupleTreeWithRepeats<P, std::index_sequence<Is...>, Tuples...>`
其中 Is...
将指示重复使用每个元组的次数,直到移动到下一个元组。请注意 <Is...> = <1,1,...,1>
将减少为与 TupleTree<P, Tuples...>
相同。我遇到的歧义是这两个专业:
TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber, P<Ts...>, First, Rest...>
TupleTreeWithRepeatsHelper<P, std::index_sequence<I, Is...>, I, P<Ts...>, First, Rest...>
出于某种原因,P<Ts...>
的存在会导致歧义,因为当我将其替换为单一命名类型时,歧义就消除了。即使我将 std::index_sequence<Is...>
替换为 std::index_sequence<I, Is...>
,歧义仍然存在。这是怎么回事?以及如何解决这个问题?这是代码,与 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
}
据我所知,这两个应该没有歧义。
无论如何,一个简单的解决方法是根据 "without repeats":
实施 "with repeats"
// 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
是
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。有人可能会争辩说这也是一种更好的设计。
最小化为:
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 报告了一个歧义,这对我来说绝对像是一个错误。 Clang 正确处理了这个问题。
虽然 T.C. 的解决方法肯定更优雅,但我发现的这个解决方法可能是一般的解决方法,只要遇到这个 GCC 错误,它应该总是有效。我没有回避 GCC 错误,而是直面它。只需将 P<Ts...>
替换为单个类型名称并定义一个单独的元函数来使用它:
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...> {};
完整代码:
#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
}
给定
template <typename...> struct Pack;
using T1 = std::tuple<int, char, double>;
using T2 = std::tuple<bool, double, int, char>;
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>
>
这扩展到任意数量的元组。定义足够容易理解吗?好的,我的程序运行正常:
但现在我想将定义扩展到
`TupleTreeWithRepeats<P, std::index_sequence<Is...>, Tuples...>`
其中 Is...
将指示重复使用每个元组的次数,直到移动到下一个元组。请注意 <Is...> = <1,1,...,1>
将减少为与 TupleTree<P, Tuples...>
相同。我遇到的歧义是这两个专业:
TupleTreeWithRepeatsHelper<P, std::index_sequence<Is...>, LoopNumber, P<Ts...>, First, Rest...>
TupleTreeWithRepeatsHelper<P, std::index_sequence<I, Is...>, I, P<Ts...>, First, Rest...>
出于某种原因,P<Ts...>
的存在会导致歧义,因为当我将其替换为单一命名类型时,歧义就消除了。即使我将 std::index_sequence<Is...>
替换为 std::index_sequence<I, Is...>
,歧义仍然存在。这是怎么回事?以及如何解决这个问题?这是代码,与 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
}
据我所知,这两个应该没有歧义。
无论如何,一个简单的解决方法是根据 "without repeats":
实施 "with repeats"// 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
是
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。有人可能会争辩说这也是一种更好的设计。
最小化为:
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 报告了一个歧义,这对我来说绝对像是一个错误。 Clang 正确处理了这个问题。
虽然 T.C. 的解决方法肯定更优雅,但我发现的这个解决方法可能是一般的解决方法,只要遇到这个 GCC 错误,它应该总是有效。我没有回避 GCC 错误,而是直面它。只需将 P<Ts...>
替换为单个类型名称并定义一个单独的元函数来使用它:
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...> {};
完整代码:
#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
}