An Issue about the rule of explicit specialization where mentioned implicit instantiation - c++

#include <iostream>
template<class T> struct A {
enum E : T;
};
template<class T> enum A<T>::E : T { eT };
template<> enum A<char>::E : char { //#1
echar
};
int main(){
}
Consider the above code which is mentioned in temp.expl.spec#6, it's a typically ill-formed code. The rule says:
If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
At the point #1, where the specialization A<char> will cause implicit instantiation
As a contrast:
#include <iostream>
template<class T> struct A {
struct Test;
};
template<typename T>
struct A<T>::Test{
};
template<>
struct A<int>::Test{ //#2
int c;
};
int main(){
}
Consider this code, At the point #2, where the specialization A<int> also cause implicit instantiation, The difference of the implicit instantiation between such two codes is said as the following:
temp.inst#2
The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or noexcept-specifiers of the class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. However, for the purpose of determining whether an instantiated redeclaration is valid according to [basic.def.odr] and [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition.
Because in the first code, the member enumeration is unscoped, hence the implicit instantiation of A<char> will also implicit instantiated the definition for enum E. As a contrast, in the second, Test is a member class, hence the implicit instantiation of specialization A<int> will only cause implicit instantiation of the declaration for member class Test rather than definition.
As a variant case of the first code:
#include <iostream>
template<class T> struct A {
enum E : T;
};
template<> enum A<char>::E : char {
echar
};
template<> enum A<int>::E : int { //#1
echar
};
int main(){
}
The difference from the first code is that there's no definition for member enumeration E of its enclosing primary class template, within the primary class template A, it's just a declaration.
So, I think the rule in [temp.expl.spec#6] is not clear. As these above codes shown, in each case, the specialization of primary class template all cause the implicit instantiation for class member, The distinction is one expect the implicit instantiation of declaration , the other is definition.
So, I think modified the rule as the following:
If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation of the definition that instantiated from the definition for the corresponding entity, in every translation unit in which such a use occurs; no diagnostic is required.
It will be conform the observable result. If I miss something, please correct me.

Related

Does an explicit instantiation declaration of a member function of a class template cause instantiation of the class template?

[dcl.spec.auto]/14 states [emphasis mine]:
An explicit instantiation declaration does not cause the instantiation of an entity declared using a placeholder type, but it also does not prevent that entity from being instantiated as needed to determine its type. [ Example:
template <typename T> auto f(T t) { return t; }
extern template auto f(int); // does not instantiate f<int>
int (*p)(int) = f; // instantiates f<int> to determine its return type, but an explicit
// instantiation definition is still required somewhere in the program
 — end example ]
and [temp.explicit]/11 states [emphasis mine]:
An entity that is the subject of an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit instantiation in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required.
Now, consider the following program:
template <class T>
struct Foo {
static const auto& foo() { static T t; return t; }
};
// explicit instantiation declarations
extern template const auto& Foo<void>::foo();
extern template const auto& Foo<int>::foo();
int main() {}
This is well-formed; [temp.explicit]/11 does not apply as neither member function of class template specialization entities Foo<void>::foo() nor Foo<int>::foo() are used in a way that would otherwise cause an implicit instantiation, as per [dcl.spec.auto]/14(1).
Now, consider if we defined a friend function at its friend declaration in the class template Foo:
template <class T>
struct Foo {
static const auto& foo() { static T t; return t; }
friend void bar() { }
};
void bar();
If any more than one specialization of Foo is instantiated in the same translation unit, [basic.def.odr]/1 will be violated:
No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, or template.
as the friend bar() would be re-defined(2) for each specialization that is instantiated.
According to the argument above, the explicit instantiation declarations of the two member function (of class template) specializations should not lead to any instantiation of the associated class template (as per [dcl.spec.auto]/14), meaning the following program should also arguably be well-formed:
template <class T>
struct Foo {
static const auto& foo() { static T t; return t; }
friend void bar() { }
};
void bar();
extern template const auto& Foo<void>::foo();
extern template const auto& Foo<int>::foo();
int main() {}
However, both Clang (10.0.0) and GCC (10.1.0) rejects the program (C++14, C++17, C++2a) with a "redefinition of void bar()” error:
Clang
error: redefinition of bar
note: in instantiation of template class Foo<int> requested here:
extern template const auto& Foo<int>::foo();
GCC
In instantiation of struct Foo<int>:
error: redefinition of void bar()
But I never requested (or, afaict, used these specializations in a way such that) the Foo<int> or Foo<void> specializations (are) to be instantiated.
Thus to the question:
Is the program (with the friend) above well-formed, or are the compilers correct to instantiate the class template specializations and subsequently reject the program?
(1) Note the the same question (and compiler behaviour) applies even if foo() is not declared using a placeholder type, but then we would not be able to fall back on the explicitness of [dcl.spec.auto]/14, but we may not need to.
(2) As friends defined at their friend declaration are inline, we may actually instantiate different specializations in different translation units and still respect ODR, but this is not relevant in this discussion.
The argument that the class template must be instantiated is that declaration matching may need to know things about the class that plainly require instantiation. Consider the simplified example
template<class T>
struct A {void f(T) {}};
extern template void A<int>::f(int);
To know whether the member function exists, we must instantiate the declaration in the class template, and we can’t do that in general without instantiating the whole class: the parameter type could depend on any other declarations in the class template, and we might need to consider multiple overloads or even do template argument deduction to decide which f is meant. One can argue that instantiation should happen only if one of these situations actually pertains, which strays into CWG2 territory (where instantiation is obviously impossible), but the idea is that instantiation is necessary in principle to decide about such questions because we simply don’t try examining the template itself first.

When a class template is instantiated, is a member template declaration also instantiated?

So let's take a example :
template<typename T> struct foo
{
template<typename X> void bar(void (T::*)()) {}
template<typename X> void bar(X*) {}
template<typename X> void bar(T**) {}
};
int main() { foo<int> p; }
Will above instantiation of class foo<int> instantiate member template declaration bar even though member itself is template? Any reference from standard will be much appreciated.
The answer is yes.
Citation from C++11 standard, [temp.inst] [14.7.1]:
...
The implicit instantiation of a class template specialization causes the implicit instantiation of the
declarations, but not of the definitions, default arguments, or exception-specifications of the class member
functions, member classes, scoped member enumerations, static data members and member templates; and
it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions.
The second part has some more info about exactly when the member template definitions are instantiated. (It generally happens implicitly, when they are used:)
Unless a member of a class template or a member template has been explicitly instantiated or explicitly
specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated
side-effects) of a static data member does not occur unless the static data member is itself used in a way
that requires the definition of the static data member to exist.

Member class instantiation

N4296::14.7.1/1 [temp.inst] told us the following:
The implicit instantiation of a class template specialization causes
the implicit instantiation of the declarations, but not of the
definitions, [...], member classes, [...]
What is that rule about? Let me give you an example:
template<class T>
class A
{
public:
template<class W> class Y; //1, declaration
template<class V> class U{ V v; }; //2, definition
};
A<int> a; //3, implicit instantiation
int main(){ }
Does implicit instantiation at //3 cause implicit instantiation at //2 and at //1? If so, what template argument was used to instantiate those member classes?
There is nothing special about those member templates, compared to the "outer" template. The compiler reads them as a declaration, such that it knows that there exist names as A<T>::Y<W> and A<T>::U<V>, much the same as when you declare the template for class A:
template <typename T>
class A {
int a;
};
Which also only declares the existence of a class A<T> but does not instantiate it.
Instantiation is deferred until a templated type is actually used (or explicitly instantiated), which equally applies to the member templates.

Implicit instantiation of the template within its own definition

Let's consider simple example about template implicit instantiation:
#include <iostream>
template<int N>
class A
{
static const int a = A<N-1>::a; //1, OK, doesn't require implicit intantiation
};
template<int N>
class B
{
static const int a = B<1>::a; //2, Error, implicit instantiation of template 'B<1>' within its own definition
};
int main(){ }
DEMO
The standard wasn't clear about that fact. What it says is only that N3797::14.7.1/1 [temp.inst]:
Unless a class template specialization has been explicitly
instantiated (14.7.2) or explicitly specialized (14.7.3), the class
template specialization is implicitly instantiated when the
specialization is referenced in a context that requires a
completely-defined object type or when the completeness of the class
type affects the semantics of the program.
Neither 1 nor 2 don't require the class type to be completely defined, however the second one causes the error about implicit instantiation. I'd like to understand why.
I think the reason B<1> fails immediately and A<N-1> not is because of temp.res#general-8.4
The validity of a template may be checked prior to any instantiation. The program is ill-formed, no diagnostic required, if:
a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter

Implicit instantiation depending on scoped or unscoped enumeration

14.7.3/6 says the following:
If a template, a member template or the member of a class template is explicitly specialized then that
specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined.
It then gives the following example:
template<class T> struct A
{
enum E : T;
enum class S : T;
};
template<> enum A<int>::E : int { eint }; // OK
template<> enum class A<int>::S : int { sint }; // OK
template<class T> enum A<T>::E : T { eT };
template<class T> enum class A<T>::S : T { cT };
template<> enum A<char>::E : char { echar }; // ill-formed, A<char>::E was instantiated
// when A<char> was instantiated
template<> enum class A<char>::S : char { schar }; // OK
I'm not following how it is able to compile the last line but not the one before it. I expected both examples to fail because both A<char>::E and A<char>::S were being implicitly instantiated. I notice when I remove class from the enum-key of S in the primary template the last line fails with the same message as the other. Why does this happen?
Implicit instantiation of a class template implicitly instantiates the definitions of unscoped member enumerations, but only the declarations of scoped member enumerations.
§14.7.1 [temp.inst]/p1 (emphasis added):
The implicit instantiation of a class template specialization causes
the implicit instantiation of the declarations, but not of the
definitions, default arguments, or exception-specifications of the
class member functions, member classes, scoped member
enumerations, static data members and member templates; and it
causes the implicit instantiation of the definitions of unscoped
member enumerations and member anonymous unions.