Reputation: 10557
What is the procedure of comparing the class template
specializations? The standard is not detailed on this point (or I am missing the right place).
My question has NOTHING TO DO with deciding what specialization to use during the instantiation. Please, do not comment on that. The question is about comparing the specializations with each other to decide if particular specialization is already defined or not yet.
Consider this sample code:
template <class x1, class x2>
struct CoreTemplate { };
template <class x1, class x2>
struct CoreTemplate<x1*, x2*> { int spec; CoreTemplate() { spec = 1; } };
template <class x1, class x2>
struct CoreTemplate<x2*, x1*> { int spec; CoreTemplate() { spec = 2; } };
int main(int argc, char* argv[])
{
CoreTemplate<int*, int*> qq;
printf("var=%d.\r\n", qq.spec);
}
When I try to compile this code with MSVC, I get an error for the instantiation attempt inside the main
function:
cpptest1.cxx(15) : error C2752: '
CoreTemplate<x1,x2>
' : more than one partial specialization matches the template argument list
For me it would be more logical to issue an error for an attempt to declare identical template specializations. I do not see any difference between the specializations above.
So, does anybody know rules of comparing template specializations? Articles, links, books, etc will also help.
Upvotes: 10
Views: 382
Reputation: 299940
Ah, but they are not the same because they do not use the same parameters. Using clang and your original example:
#include <cstdio>
template <class x1, class x2>
struct CoreTemplate { };
template <class x1, class x2>
struct CoreTemplate<x1*, x2*> { int spec; CoreTemplate() { spec = 1; } };
// note: partial specialization matches [with x1 = int, x2 = int]
template <class x1, class x2>
struct CoreTemplate<x2*, x1*> { int spec; CoreTemplate() { spec = 2; } };
// note: partial specialization matches [with x1 = int, x2 = int]
int main()
{
CoreTemplate<int*, int*> qq;
// error: ambiguous partial specializations of 'CoreTemplate<int *, int *>'
std::printf("var=%d.\r\n", qq.spec);
}
However if we tweak the partial specializations so that they exactly match:
template <class x1, class x2>
struct Core { };
template <class x1>
struct Core<x1*, x1*> { int spec; Core() { spec = 1; } };
// note: previous definition is here
template <class x1>
struct Core<x1*, x1*> { int spec; Core() { spec = 2; } };
// error: redefinition of 'Core<type-parameter-0-0 *, type-parameter-0-0 *>'
Therefore, it just seems to be a quality of implementation issue. A compiler could emit a warning for the first case, but it might be resource consuming in the general case or it may just be that nobody expressed the need so far.
Upvotes: 1
Reputation: 490198
The standard is specific in stating that this only happens when you attempt to instantiate the template (§14.5.4.1/1):
When a class template is used in a context that requires an instantiation of the class, it is necessary to determine whether the instantiation is to be generated using the primary template or one of the partial specializations. [emphasis added]
Unfortunately, the rest of your question can't be answered without discussing how to decide which specialization to use during instantiation. Here's the text from the standard (continuing from the excerpt above):
This is done by matching the template arguments of the class template specialization with the template argument lists of the partial specializations.
- If exactly one matching specialization is found, the instantiation is generated from that specialization.
- If more than one matching specialization is found, the partial order rules (14.5.4.2) are used to determine whether one of the specializations is more specialized than the others. If none of the specializations is more specialized than all of the other matching specializations, then the use of the class template is ambiguous and the program is ill-formed.
So, it never even attempts to compare the templates directly to each other at all. Rather, it attempts to find a specialization that will match the arguments given. If more than one matches, it attempts to pick the most specialized one based on the partial ordering rules. If neither is more specialized than the other, then the instantiation is ambiguous, and compilation fails.
Now, it's certainly true that neither of these specializations could ever be used, since there would always be ambiguity -- if either matches, the other obviously matches equally well. There's simply no requirement for the compiler to detect or diagnose that though. In this exact case (essentially identical specializations) that would probably be easy, but there are almost certainly other cases where it would be much more difficult, so (apparently) the committee decided the compiler didn't even have to try.
Upvotes: 5
Reputation: 70000
For me it would be more logical to issue an error for an attempt to declare identical template specializations.
That would not happen, because CoreTemplate<int*, double*>
and CoreTemplate<double*, int*>
will generate different types.
Below is my guess:
Compiler may not do the extra sanity/common-sense checks for template
bodies.
Once you instantiate a template
, at that time compiler looks up for the matching type and picks up the best one. If it doesn't match only one, then it gives compiler error either for no-match or multiple-match.
For example:
template<typename T>
void foo ()
{
T::x();
}
int main ()
{
}
Would compile fine, even though we know that in whole C++ program there is not a single function name x()
. Compiler will give error only when you try instantiating the foo<T>
.
Also if you twist your example a bit by putting main()
between two specializations, it will compile perfectly fine.
Upvotes: 0