Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why compiler doesn't give error while defining similar template specializations?

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.

like image 566
Kirill Kobelev Avatar asked Jun 21 '12 06:06

Kirill Kobelev


2 Answers

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.

like image 61
Jerry Coffin Avatar answered Oct 13 '22 00:10

Jerry Coffin


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.

like image 23
Matthieu M. Avatar answered Oct 12 '22 23:10

Matthieu M.