6

Following code gives compiler error which is expected (Demo):

  1 template<bool> struct Range;
  2 
  3 template<int value, typename = Range<true> > struct Unique;
  4 template<int value> struct Unique<value, Range<(value > 1)> > { typedef char type[1]; };
  5 template<int value> struct Unique<value, Range<(value > 2)> > { typedef char type[2]; };
  6 
  7 Unique<3>::type o1;
  8 Unique<3>::type o2;

Now, if I swap line-5 and line-7. Then there is NO compiler error !! Demo.

  5 Unique<3>::type o1;

  7 template<int value> struct Unique<value, Range<(value > 2)> > { typedef char type[2]; };

For o1, it's understandable to have no error, because specialization for (value > 2) is not yet visible. But why there no error for o2 also, which sees 2 matching specializations !?
My guess is that, compiler should be choosing the Unique<3>::type with some arbitrary name when it encounters for the 1st time and then replacing Unique<3>::type everywhere with that name.

Is this a compilation bug or C++ bug or C++ "feature" ?

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 3
    I would GUESS (so don't tear me to shreds) that it's just like you said; because the two o1 and o2 are the same type, the compiler simply doesn't bother recreating the type. (Same for me using MS dev studio 2005.) – Grimm The Opiner Mar 06 '12 at 11:02
  • 1
    @iammilind Clang behaves the same. – R. Martinho Fernandes Mar 06 '12 at 11:50
  • @R.MartinhoFernandes & user1158692, thanks. Actually I don't have availability of other compilers (I removed my comment, as it was sounding inappropriate). It seems that 3 major compilers gcc, msvc, clang are behaving in same way, then this can be considered as universal compiler bug! – iammilind Mar 06 '12 at 12:10
  • is this code in file-scope or in function-scope? this is interesting to discuss the point of instantiation – Sebastian Mach Mar 06 '12 at 14:02
  • @phresnel, I use this as part of a macro in file scope. – iammilind Mar 06 '12 at 16:20

3 Answers3

3

A template is instantiated the first time it is needed (in the translation unit), not each time.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 1
    However, before `o2` another specialization is introduced and added to the set of visible template declarations, making the declaration of `o2` ambiguous as is, yielding an ill-formed program. Whether a template was instantiated already does not matter before the Standard. – Sebastian Mach Mar 06 '12 at 11:32
  • 1
    @phresnel That's not what the standard says. Every template has a point of instantiation. After that, it's not a template anymore; just a class or a function with a funny name. – James Kanze Mar 06 '12 at 12:32
  • @JamesKanze: Although I tend to agree with you, I cannot find any explicit mention of this behavior in the Standard. Could you substantiate your answer ? – Matthieu M. Mar 06 '12 at 13:06
  • @MatthieuM. The point of instantiation is in 14.6.4.1 in C++11. As for "once instantiated, it's not a template, but something else", that's implicit in the whole concept of templates: you can't call a template, only a function (which may be the result of instantiating a template), etc. – James Kanze Mar 06 '12 at 13:48
  • Can you maybe elaborate on your suggestions, in relation to the question's code? I might not be getting the point, are you saying no error in code 2 is correct? Your answeris really terse. Also, are you suggesting that declarations/instantiations may hide further template specializations? – Sebastian Mach Mar 06 '12 at 14:09
  • @phresnel: I would say that this falls under the category of *no diagnostic required*, that is, the specialization between `o1` and `o2` is just silently ignored by the compiler. – Matthieu M. Mar 06 '12 at 14:15
  • @MatthieuM.: While I can smell the "why" (see my comment-conversation with n.m.) I still miss the "why does it not see it". – Sebastian Mach Mar 06 '12 at 14:19
  • @phresnel I'm fairly certain that no error for `o2` is "correct". I think the intent is as I said: that the template have a single point of instantiation, and after that, the name `Unique<3>` is a class, and that the template shouldn't be reinstantiated. Alternatively, if reinstantiation is allowed (it's not that clear in the standard), the results of the two instantiations must be the same, or you have undefined behavior. (Which means that the compiler can accept it without an error message.) – James Kanze Mar 06 '12 at 14:31
  • @JamesKanze: Tbh, this is not one of my fittest days :/ I really missed to expand it to the template-id, and so missed your point. I think I will expand my answer, too. – Sebastian Mach Mar 06 '12 at 15:11
  • Can you put the relevant quotes from standard. I am not getting exactly which quotes are being referred. – iammilind Mar 07 '12 at 01:55
  • @iammilind Not easily. There's about a half a page on point of instantiation, to start with. This is one of those things that the standard doesn't specify clearly with a single statement, but that is spread out over large parts of the standard. (That's why it isn't 100% certain whether we have "legal code" or "undefined behavior". I think it's legal code, but other interpretations are possible.) – James Kanze Mar 07 '12 at 08:47
2

In 14.5.5.1 Matching of class template partial specializations, there is

If more than one matching specialization is found, the partial order rules (14.5.5.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.

However, this would only apply to your first case where there are two specializations visible, and I am not sure yet if those two specializations are valid in themselves.

In your second case, however, before the second specialization is reached, the template-id Unique<3> already exists, for which (thanks n.m., Matthieu M., James Kanze) the first specialization is already instantiated:

14.5.5 Class template partial specializations

A partial specialization shall be declared before the first use of a class template specialization that would make use of the partial specialization as the result of an implicit or explicit instantiation in every translation unit in which such a use occurs; no diagnostic is required.

And in 14.5.5, Item 8

Within the argument list of a class template partial specialization, the following restrictions apply:

— A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier. [ >Example:

template <int I, int J> struct A {};

template <int I> struct A<I+5, I*2> {}; // error

template <int I, int J> struct B {};

template <int I> struct B<I, I> {}; // OK

—end example ]

So it seems that non-type arguments do not participate in specialization creation, if not used as a simple identifier (thus Range<(value > 2)> would be wrong).

So it seems your code is not well-formed.


Not directly related but still interesting in this regard:

14.7.3 Explicit specialization

The placement of explicit specialization declarations for function templates, class templates, member functions of class templates, static data members of class templates, member classes of class templates, member class templates of class templates, member function templates of class templates, member functions of member templates of class templates, member functions of member templates of non-template classes, member function templates of member classes of class templates, etc., and the placement of partial specialization declarations of class templates, member class templates of non-template classes, member class templates of class templates, etc., can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
  • @R.MartinhoFernandes: Amusing indeed, regarding it's an ISO standard. – Sebastian Mach Mar 06 '12 at 11:40
  • No, in the second case the second specialization is not visible at the point of instantiation, so 14.5.5.1 does not seem to apply here. I think this one is more pertinent: 14.5.5 A partial specialization shall be declared before the first use of a class template specialization that would make use of the partial specialization as the result of an implicit or explicit instantiation in every translation unit in which such a use occurs; no diagnostic is required. – n. m. could be an AI Mar 06 '12 at 11:55
  • @n.m.: I was refering to "But why there no error for o2 also, which sees 2 matching specializations !?" – Sebastian Mach Mar 06 '12 at 13:54
  • o2 sees no specialization at all. The instantiation happens only once, when o1 is declared. – n. m. could be an AI Mar 06 '12 at 13:57
  • @n.m.: So you suggest that both specializations are equivalent to the compiler? Maybe expand this into a real answer, I'd be glad to increase my knowledge. – Sebastian Mach Mar 06 '12 at 14:18
  • @n.m: I would suggest you format this quote of `14.5.5/1` into a proper answer, this is exactly what I had been looking for and the *no diagnostic required* just explains why the compiler is silent about this. – Matthieu M. Mar 06 '12 at 14:25
2

o1 doesn't see the second specialization because of this:

14.5.5/1 A partial specialization shall be declared before the first use of a class template specialization that would make use of the partial specialization as the result of an implicit or explicit instantiation in every translation unit in which such a use occurs; no diagnostic is required.

In the second example, the second specialization would be used in the instantiation of Unique<3> if it were seen before the declaration of o1. Since this rule is violated, the program is broken, and the compiler is allowed to be silent about it.

o2 doesn't see the second specialization because it doesn't see any specialization at all. Its class is instantiated once, at the point of o1 declaration.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243