While trying to implement a few things relying on variadic templates, I stumbled accross something I cannot explain. I boiled down the problem to the following code snippet:
From temp.class.order:
For two class template partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, the first function template is more specialized than the second according to the ordering rules for function templates ([temp.func.order]):
Each of the two function templates has the same template parameters as the corresponding partial specialization.
Each function template has a single function parameter whose type is a class template specialization where the template arguments are the corresponding template parameters from the function template for each template argument in the template-argument-list of the simple-template-id of the partial specialization.
The order of:
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I'm variadic!" << std::endl;
}
};
depends on the order of:
template <template <typename...> class Z, typename T>
void bar(test<Z, Z<T>>); // #1
template <template <typename...> class Z, typename T, typename ... Args>
void bar(test<Z, Z<T, Args...>>); // #2
From [temp.func.order]:
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs ([temp.variadic]) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.
Using the transformed function template's function type, perform type deduction against the other template as described in [temp.deduct.partial].
By those paragraph, for any function transformed from any synthesized template Z0
and type T0
, which can form #1
, we can do type deduction with #2
. But functions transformed from #2
with fictitious template Z2
with any type T2
and any non-empty set of Args2
can't be deduced from #1
. #1
is obviously more specialized than #2
.
clang++ is right in this case.
Actually, this one and this one are failed to compile (because of ambiguous) in both g++ and clang. It seems like both compilers have hard time with template template parameters. (The latter one is clearly ordered because its order is the same of no function call).