Why is this C ++ specialization code invalid?

(Note: I know how illegal it is, I am looking for the reason why the language does this.)

template<class c> void Foo(); // Note: no generic version, here or anywhere. int main(){ Foo<int>(); return 0; } template<> void Foo<int>(); 

Mistake:

 error: explicit specialization of 'Foo<int>' after instantiation 

A quick go from Google found this quote from the spec , but it only offers what, not why.

Edit:

Several answers passed the argument (for example, confirmed my assumption) that this is the rule, because otherwise the One Definition Rule (ODR) would be violated. However, this is a very weak argument, because in this case it fails for two reans:

  • Moving an explicit specialization to another translation system solves the problem and does not seem to violate ODR (or, as the linker says).
  • The short form of ODR (in relation to functions) is that you cannot have more than one body for any given function, but I do not. The only place where the function body is defined is in explicit specialization, so calling Foo<int> cannot determine the general specialization of the template, because there is no specialized specialized body.

Speculation on this:

Guess why the rule exists at all: if the first line suggested a definition (as opposed to a declaration), explicit specialization after instantiation would be a problem because you would get several definitions. But in this case, the only definition in sight is explicit specialization.

Oddly enough, the following works (or something like this in the real code I'm working on):

File A:

 template<class c> void Foo(); int main(){ Foo<int>(); return 0; } 

File B:

 template<class c> void Foo(); template<> void Foo<int>(); 

But using it generally begins to create a spaghetti import structure.

+7
source share
3 answers

Guess why the rule exists in everything: if the first line suggested (as opposed to a declaration), explicit specialization after creation would be a problem, because you get multiple definitions. But in this case, the only definition in sight is sheer specialization.

But you have a few definitions. You already defined Foo <int> when you create the instance, and after that you try to specialize the template function for the int that is already defined.

 int main(){ Foo<int>(); // Define Foo<int>(); return 0; } template<> void Foo<int>(); // Trying to specialize already defined Foo<int> 
+5
source

This code is illegal because an explicit specialization appears after the instance. Basically, the compiler first saw a generic template, then saw an instance of it and specialized in this generic type template with a specific type. After that, he saw a concrete implementation of the generic template that had already been created. So what should the compiler do? Go back and recompile the code? That is why this is not allowed.

+2
source

You should think of explicit specialization as a function declaration. Just as if you had two overloaded functions (not templated), if only one declaration could be found before you try to call the second version, the compiler will say that it cannot find the required overloaded version. The difference with templates is that the compiler can generate this specialization based on a template of common functions. So why is it forbidden? Since the full specification of the template violates ODR when it is visible, since by that time there was already a specialized specialization of the template for the same type. When a template is created (implicitly or not), the corresponding specialized specialization of the template is also created, so that subsequent use (in the same translation unit) of the same specialization will be able to reuse the instance and not duplicate the template code for each instance. Obviously, ODR is also applicable to template specializations, as applicable elsewhere.

So, when the quoted text says “no diagnostics are needed”, it simply means that the compiler should not provide you with a keen remark that the problem is with the creation of the template that happened sometime before the explicit specialization, But if it is not , another option is to give a standard ODR violation error, i.e. "Multiple definitions of the specialization" Foo "for [T = int]" or something like that that would not be as useful as a smarter diagnosis.

EDIT RESPONSE

1) Although it is said that all definitions of a template function (i.e. implementation) should be visible at the time of instantiation (so that the compiler can replace the template argument and instantiate the function template). However, implicitly creating a function template only requires the declaration of the function to be available. Thus, in your case splitting it into two translation units works because it does not violate ODR (since there is only one declaration Foo<int> in this TU), the declaration if Foo<int> available at the implicit instantiation point (via Foo<T> ), and the definition of Foo<int> available to the linker in TU B. Thus, no one has argued that this second example “should not work,” it works as intended. Your question is about a rule that applies within one translation unit, does not contradict the arguments, saying that an error does not occur when you break it into two specifications (especially when it clearly should work fine in two specifications, in accordance with the rule).

2) In your first example, either there will be an error, because the compiler cannot find the template of the general function (unspecialized implementation) and, therefore, cannot create an instance of Foo<int> from the general template. Or, the compiler will find a definition for the generic template, use it to instantiate the Foo<int> , and then throw an error because the second specialized specialization Foo<int> is encountered. You seem to think that the compiler will find your specialization before it reaches it, but it is not. C ++ compilers will compile the code from top to bottom, they do not go back and forth to replace the material here and there. When the compiler gets the first use of Foo<int> , it sees only a common template at this point, assumes that this common template will be implemented, which can be used to create an instance of Foo<int> , it does not expect a specialized implementation for Foo<int> it expects and will use shared. Then he sees the specialization and gives an error, because he already decided that the general version should be used, so she sees two different definitions for the same function, and yes, it violates ODR. It is so simple.

WHY HE WHY !!!

Case 2 TU should work, because you should be able to exchange templates between TU, C ++ function and useful (in case you have a small number of possible instances, you can precompile them).

It is not possible to resolve 1 case of TU, ​​since declaring something in C ++ tells the compiler that "this thing is defined somewhere." You tell the compiler “somewhere the general definition of the template”, and then say “I want to use the general definition to create the Foo<int> function,” and finally you say: “When Foo<int> is called, it must use this special definition. " This is a contradiction! This is why ODR exists and is applied to this context to prohibit such contradictions. It does not matter if there is no general definition of "to-be-found", the compiler expects it, and it must be assumed that it exists and that it differs from specialization. He can’t go on and say “okay, so I’ll look everywhere in the code for a general definition, and if I can’t find it, then I will go back and“ approve ”this specialization, which will be used instead of the general one, but if I find it, I I will mark this specialization as a mistake. " He also cannot ignore the desire of the programmer by modifying code that clearly shows his intention to use a common template (since the specialization has not yet been announced) for code that uses the specialization that appears later. I can’t explain the “why” more clearly than that.

Case 2 TU is completely different. When the compiler compiles TU A (which uses Foo<int> ), it will search for a common definition, will not be able to find it, suppose that it will be linked later, like Foo<int> , and leave a character placeholder. Then, since the linker will not look for patterns (patterns are NOT exported in practice), it will look for a function that implements Foo<int> , and it does not care if it is a specialized version or not. Linker is satisfied while he finds the same character for the link. This is because it would be a nightmare to do it differently (to look for discussions on "exported templates") for programmers (not having the ability to easily change functions in their compiled libraries) and for compiler providers (to implement this crazy connecting scheme).

+2
source

All Articles