For the code below I get an ambiguous template instantiation error with gcc. However, using Clang or Visual Studio the Code compiles fine. A full working example of the code can be found here: http://coliru.stacked-crooked.com/a/60ef9d73ce95e6f9
I have a class template that is build from an aggegate type
template<template<typename...> typename AggregateType, typename ...>
struct MyClass;
The aggregate type is composed from a list of base classes, for example
template<typename ... Bases>
struct Aggregate : Bases...
{ };
I have defined two specialzations of MyClass. The first specialization is the common case and reads
// specialization for two argument list for the
// aggregate type
template<template<typename...> typename AggregateType,
typename Base,
typename ... Bases1,
typename ... Bases2>
struct MyClass<
AggregateType,
AggregateType<Bases1...>,
AggregateType<Base, Bases2...>>
{
void func()
{
std::cout << "not specialized\n";
}
};
A second specialization handels the case when the the second base list has only 1 argument
// specialization for the second argument list with length 1
template<template<typename...> typename AggregateType,
typename Base,
typename ... Bases1>
struct MyClass<
AggregateType,
AggregateType<Bases1...>,
AggregateType<Base>>
{
void func()
{
std::cout << "specialized\n";
}
};
Using MyClass with a second argument list of length 1, I expected the compiler to choose the second specialization of MyClass, since it is the more specilized template
class Foo {};
class Bar {};
int main()
{
// this should give the not specialized class
using NotSpecialized = MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo, Bar>>;
NotSpecialized ns;
ns.func();
// this should give the specialized class
using Specialized = MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo>>;
Specialized s;
s.func();
}
While the code works fine with Clang, gcc gives an ambiguous template instantiation error. How can I circumvent this error and still use gcc? If I remove the AggregateType template argument, the code also works with gcc, see http://coliru.stacked-crooked.com/a/c1f6edd5fab7df4d
(All ISO Standard references below refer to N4659: March 2017 post-Kona working draft/C++17 DIS, and all example program results are consistent over GCC and Clang for C++11, C++14 and C++17)
I believe GCC is wrong here, but I haven't been able to find a corresponding (open) GCC bug report.
[temp.class.order]/1 covers the partial ordering of class template specializations [emphasis mine]:
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:
- (1.1) Each of the two function templates has the same template parameters as the corresponding partial specialization.
- (1.2) 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.
Thus, for analyzing ordering, we re-write the class template specializations as function templates, as per above:
// G)
template<template<typename...> typename AggregateType,
typename Base,
typename... Bases1,
typename... Bases2>
void f(MyClass<AggregateType,
AggregateType<Bases1...>,
AggregateType<Base, Bases2...>>);
// F)
template<template<typename...> typename AggregateType,
typename Base,
typename... Bases1>
void f(MyClass<AggregateType, AggregateType<Bases1...>, AggregateType<Base>>);
The partial ordering of the G and F overloads of f
is governed by [temp.func.order]/2, [temp.func.order]/3 and [temp.func.order]/4 [emphasis mine]:
[temp.func.order]/2
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.
[temp.func.order]/3
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs 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. [...]
[temp.func.order]/4
Using the transformed function template's function type, perform type deduction against the other template as described in [temp.deduct.partial]. [...]
Thus, to produce the transformed template for the two f
overloads above, specifically considering the template template parameter AggregateType
(as used in both overloads) and the instantiation of these overloads with the particular class template Aggregate
and classes Foo
and Bar
,
template<typename ... Bases>
struct Aggregate : Bases...
{ };
class Foo {};
class Bar {};
used as arguments for the template template parameter and template parameters, respectively, we may without loss of generality consider the following (partially) transformed function templates as argument templates when continuing the analysis of the partial ordering of the original class templates:
// G-transformed (argument template>
template<typename... Bases2>
void f(MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo, Bases2...>>);
// F-transformed (argument template>
void f(MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo>>);
From [temp.deduct.partial]/2, [temp.deduct.partial]/10 and [temp.deduct.partial]/11 [extracts, emphasis mine]:
[temp.deduct.partial]/2
Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type. [...] The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template.
[temp.deduct.partial]/10
Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G. F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.
[temp.deduct.partial]/11
If, after considering the above, function template F is at least as specialized as function template G and vice-versa, and if G has a trailing parameter pack for which F does not have a corresponding parameter, and if F does not have a trailing parameter pack, then F is more specialized than G.
it follows that that F is at least as specialized as G (/10), and moreover that F is more specialized than G due to the (additional) trailing parameter pack Bases2
that is present in G but not in F (/11). It may even be possible to directly apply the second part of [temp.deduct.partial]/10 to argue that F is more specialized than G as Aggregate<Foo>
is more specialized than Aggregate<Foo, Bases2...>>
.
Either way, either per /10 and /11, or per /10 alone, the Specialized
alias
using Specialized = MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo>>;
refers non-ambigiously to the "second specialization" (from OPs post) of MyClass
, specifically the specialization that was re-written to the F function template above, as this class template specialization is more specialized than the "first specialization" (the one with the additional Bases2
variadic template parameter pack).