c++ template weird optimization - c++

I wrote a singleton template class just like boost does:
template <typename _T>
class Singleton
{
public :
static _T* Instance()
{
static _T obj;
return &obj;
}
protected :
Singleton() {}
private :
struct ObjectCreator
{
ObjectCreator()
{
Singleton<_T>::instance();
}
};
static ObjectCreator object_creator;
};
template <typename _T>
typename Singleton<_T>::ObjectCreator Singleton<_T>::object_creator;
And I wrote the main function to test it.
#include "Singleton.h"
class A : public Singleton <A>
{
public:
int a;
};
int main()
{
A::Instance()->a = 2;
}
I know I mistyped Instance in ObjectCreator's constructor, the weird thing is I can compile it correctly by gcc-4.4.7, then I used clang-6.0, it hit me with the typo.
I guess gcc can do some optimization, because I did not do anything with ObjectCreator, so it ignored the error code.
I have two questions:
What should I do to make gcc report me that error (without changing my code), like add some compiler flag?
If anyone has a more reliable explanation for this? Some official doc would do.
Ps: I am aware that boost would add a do_nothing function in ObjectCreate and call it from Singleton<_T>:: Instance() to avoid this optimization.

What should I do to make gcc report that error (without changing my code), like add some compiler flag?
You could add an explicit instantiation template class Singleton<float>; (I just randomly picked float as type, but you could choose anything more appropriate) to force GCC to check for syntax. See https://gcc.godbolt.org/z/ii43qX for an example.
If you simply want the check, you could also put this explicit instanciation to a separate compilation unit by adding another cpp-file to your project.
However, doing an explicit instanciation is stronger than the implicit instanciation, as all members and methods are going to be instanciated. This behavior might be anwanted (see standard library for examples).
If anyone has a more reliable explanation for this? Some official doc would do.
Static members are not initialized implicitly until it is used in a way that its definition is required (this is very different to the explicit instanciation).
#StoryTeller found the right paragraph in the standard
14.7.1 Implicit instantiation [temp.inst]
The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates; and it causes the implicit instantiation of the definitions of member anonymous unions. 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.
EDIT
You should accept the answer of #StoryTeller as he correctly explained both aspects of your question first.

If I read the standard correctly, I don't believe your code is ill-formed (bar the use of a _T identifier). Clang going the extra mile is fantastic, but GCC isn't wrong to accept it as is.
The reason is your program only contains an implicit instantiation of your template. According to N19051 (emphasis mine):
14.7.1 Implicit instantiation [temp.inst]
1 ... The implicit instantiation of a class
template specialization causes the implicit instantiation of the
declarations, but not of the definitions or default arguments, of the
class member functions, member classes, static data members and member
templates; and it causes the implicit instantiation of the definitions
of member anonymous unions. 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.
Nothing uses object_creator in a way that requires its definition to exist. As such only the declaration is ever checked. Furthermore, only the declaration of class ObjectCreator is required to be instantiated itself, not its definition (or the definition of its constructor). This is for the same reason one can define an extern variable of a forward declared class type:
extern class C c;
The above paragraph (and the fact nothing uses object_creator) only requires the type name and object name be instantiated, to produce an effect similar to the above external declaration.
As a result GCC never has to verify instance is valid. I would say that given the above paragraph, even if you didn't have a typo, it's quite possible object_creator doesn't do what you think it does. If your code worked, it's only because the function local static obj was initialized on first use (making ObjectCreator redundant).
As for why adding an explicit instantiation (like #P i suggested) immediately causes an error. We can see here:
14.7.2 Explicit instantiation [temp.explicit]
7 The explicit instantiation of a class template
specialization also explicitly instantiates each of its members (not
including members inherited from base classes) whose definition is
visible at the point of instantiation and that has not been previously
explicitly specialized in the translation unit containing the explicit
instantiation.
When we do that, we recursively force everything to be instantiated, and as a result, checked.
1 - This is a 2005 draft. Very close to C++03, and therefore I believe appropriate given your use of GCC 4.4.7.

Related

Should a definition inside template class be instantiated if it is not used?

template <typename T>
struct A
{
static constexpr T obj {};
static constexpr bool noexcept_copy = noexcept( T{obj} );
static void UsesCopy() { T{obj}; }
static constexpr int C = 1;
};
struct NoCopy
{
constexpr NoCopy() = default;
NoCopy(const NoCopy&) = delete;
};
int main()
{
return A<NoCopy>::C;
}
The code above is successfully compiled by GCC, but Clang gives a compilation error:
tmp.cpp:6:57: error: call to deleted constructor of 'NoCopy'
static constexpr bool noexcept_copy = noexcept( T{obj} );
^~~~~~
tmp.cpp:20:16: note: in instantiation of template class 'A<NoCopy>' requested here
return A<NoCopy>::C;
^
tmp.cpp:15:9: note: 'NoCopy' has been explicitly marked deleted here
NoCopy(const NoCopy&) = delete;
^
1 error generated.
The A::UsesCopy function uses copy constructor as well, but the compiler does not complain about the usage of deleted function there. What is the difference between UsesCopy function and noexcept_copy constexpr? Both use copy constructor of NoCopy class and both are not used but the constexpr definition produces a compilation error, the function definition does not.
PS. Clang compiles the code above with -std=c++17 or -std=c++2a, but not with -std=c++11 or -std=c++14.
I think the correct approach to deal with this issue is similar to what is details in this pre-C++17 answer to a question about the order of initialization of static constexpr templated data members.
TL;DR - yes GCC, got it right, Clang tries to resolve the copy c'tor even though it is not allowed.
To summarize:
C++14
In p9.4.2.3 - Static data members, we have:
[...] A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer
in which every initializer-clause that is an assignment-expression is a constant expression. [...] The member shall still be defined
in a namespace scope if it is odr-used in the program and the namespace scope definition shall not contain an initializer.
So the declaration of static constexpr data member is just a declaration and is not a definition - even though it has an initializer. A definition is required to cause initialization, and none is provided in the original OP code.
To drive home the point, we have the previously quoted p14.7.1 - Templates - Implicit Instantiation (emphasis mine):
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 template
GCC correctly does not initialize noexcept_copy as there is no definition for it, only a decleration - it is never "defined in a namespace scope", as required by p9.4.2.3.
C++17
So what changed? Well, as far as I can tell - nothing major, but there were some changes around how static data members are defined, in C++17, by adopting P0389R2 whose purpose - as far as I understand - it so introduce "inline variables" which can be declared simply and then odr-used in multiple translation units with the same storage, i.e. there's only one instance of the variable initialized with the initializer in the declaration - this is similar to "static field initialization" in other languages such as Java and makes it easier to have singleton eager initialization.
Here's from the spec:
A declaration is a definition unless [...] it declares a non­-inline static data member in a class definition.
So we explicitly specify that inline static data member's declaration is a definition. No need for an external definition out of the class scope. This certainly makes it easier for programmers.
But what inline has to do with constexpr? The adoption of the proposal also resulted in this specification in p10.1.5.1:
A function or static data member declared with the constexpr specifier is implicitly an inline function or variable.
So this change actually makes it pretty explicit that the declaration of static constexpr bool noexcept_copy is also a definition - which we mustn't instantiate in case of an implicit template instantiation.
I'm guessing this is a strong enough signal for Clang developers to not initialize the static constexpr data members.
BTW, the example in C++17 Appendix D p.1 explains this rather explicitly:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
From this example we learn that, in C++ 2014 a declaration of a static constexpr was not a definition, and in order for the initialization to take place, you must have a definition in a namespace scope.
So Clang is wrong outputing an error in their C++14 implementation because in the OP code, not only is it wrong to implicitly instantiate the template class static data member - there isn't even a definition for it so it shouldn't have been instantiated even if it wasn't a template class.
I think this problem is referred by the following standard lines on template :
An implementation shall not implicitly instantiate a function
template, a variable template, a member template, a non-virtual member
function, a member class, a static data member of a class template, or
a substatement of a constexpr if statement, unless such instantiation
is required.
So the Clang compiler do the right things. GCC in this case erronously don't give you an error.
PS. Clang compiles the code above with -std=c++17 or -std=c++2a, but
not with -std=c++11 or -std=c++14.
In fact with the c++17 there was a change in the template standard.

Using `extern template` to prevent implicit instantiation of a template class

Consider the following code snippet:
template <typename>
struct X { };
extern template struct X<int>;
int main()
{
X<int>{};
}
It compiles and links: live example on godbolt.org. I would expect it not to link due to the extern template declaration.
My understanding is that extern template means: "please don't instantiate this particular template specialization in this TU, it will be provided by some other TU and you can link against it".
The examples/descriptions. I've seen on isocpp and cppreference seem to validate my mental model. E.g.
From https://en.cppreference.com/w/cpp/language/class_template:
An explicit instantiation declaration (an extern template) skips implicit instantiation step: the code that would otherwise cause an implicit instantiation instead uses the explicit instantiation definition provided elsewhere (resulting in link errors if no such instantiation exists). This can be used to reduce compilation times by explicitly declaring a template instantiation in all but one of the source files using it, and explicitly defining it in the remaining file.
Why does my code snippet link? What is actually happening here?
EDIT - found this in the latest Standard draft:
[temp.explicit]
If an entity is the subject of both an explicit instantiation declaration and an explicit instantiation definition in the same translation unit, the definition shall follow the declaration. 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.
Does this mean that the code snippet I posted is ill-formed, NDR?
Why does my code snippet link? What is actually happening here?
Well, there's nothing to link. For one has to consider the effects of the explicit instantiation. From n3337:
[temp.explicit] (emphasis mine)
10 Except for inline functions and class template
specializations, explicit instantiation declarations have the effect
of suppressing the implicit instantiation of the entity to which they
refer. [ Note: The intent is that an inline function that is the
subject of an explicit instantiation declaration will still be
implicitly instantiated when odr-used ([basic.def.odr]) so that the
body can be considered for inlining, but that no out-of-line copy of
the inline function would be generated in the translation unit. — end
note ]
So the implicit instantiation of the class template specialization X<int>, is not suppressed. It's also an aggregate, so its initialization occurs inline, and we get nothing to link against. However, if it had any members, those would be suppressed under paragraph 8:
An explicit instantiation that names a class template specialization
is also an explicit instantiation of the same kind (declaration or
definition) of each of its members (not including members inherited
from base classes) that has not been previously explicitly specialized
in the translation unit containing the explicit instantiation, except
as described below.
So if you had instead of an aggregate something akin to this:
template <typename>
struct X {
X();
};
template <typename T>
X<T>::X() {}
extern template struct X<int>;
int main()
{
X<int>{};
}
That would fail as you expect, since it ODR uses a constructor whose definition is never instantiated. The declaration is instantiated, because the enclosing specialization is instantiated, as mentioned above. But we never get any definition, under the suppressing effect of the explicit instantiation declaration.
Does this mean that the code snippet I posted is ill-formed, NDR?
Yes, by the exact sentence from [temp.explicit]/13 that you quoted. "An entity" means just that. It does not matter if an explicit instantiation declaration otherwise have no normative effect.

When are template non-static data member initialisers instantiated?

Here is a short self-contained test case to explain my question. GCC accepts this code, but clang and Intel reject it:
template <typename T>
struct false_t {
static const bool value = false;
};
template <typename T>
int f() {
static_assert(false_t<T>::value, "");
return 0;
}
template <typename T>
struct S {
int m = f<T>();
};
int s = sizeof(S<int>);
Or, based on pmr's comment, here is a simpler example which too is accepted by gcc and rejected by clang:
struct S;
template <typename T> struct X { int x = T(); };
int s = sizeof(X<S>);
sizeof(S<int>) (or sizeof(X<S>)) is supposed to instantiate the bits of the class it needs, but the compilers disagree on which bits those are. Since a non-static data member initializer would only be used by a constructor, GCC performs the instantiation as part of instantiating the class's constructor. clang and Intel do so earlier.
I'm having trouble understanding what the standard says in [temp.inst]p1:
The implicit instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or default arguments, 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.
because I don't see where even the declarations of the non-static data members (with or without initialisers) get instantiated at all.
A bit more details about where I encountered this: I was trying to create a template helper class (that would never be created at runtime) containing a member initialised using std::declval<T>() (which, I should add, I now realise wouldn't be of much use anyway), and libstdc++'s implementation of std::declval contains a static assertion like the one in my example. I can work around the problem without much effort by avoiding std::declval, but I would like to know what the standard requires.
While attempting to figure out how to ask this question, I stumbled upon the answer, but thought it might be useful to post anyway.
This is one of the open issues of the C++ standard, issue 1396 to be precise. The intent is that the initialisers only get instantiated as needed:
Non-static data member initializers get the same late parsing as member functions and default arguments, but are they also instantiated as needed like them? And when is their validity checked?
Notes from the October, 2012 meeting:
CWG agreed that non-static data member initializers should be handled like default arguments.
but there are quite a number problems with that approach that are still being resolved. Until they are resolved, it's only natural that different compilers perform the instantiation at different times, and code that requires specific behaviour should be rewritten to avoid such a requirement. In my case, that means not using std::declval.

Does casting to a pointer to a template instantiate that template?

static_cast<the_template<int>*>(0) - does this instantiate the_template with type int?
The reason for asking is the following code, which will error at linking time with an undefined reference to check_error<char>(void*, long) with Clang and GCC 4.4.5, indicating that it does not instantiate the template. MSVC and GCC 4.5.1 however compile and link just fine, leading to the believe that it does instantiate the template. However, if you leave out the cast, MSVC and GCC (both 4.4.5 and 4.5.1) will error on check_error<char> only (the wanted behaviour), while Clang will error on both calls. Normally I believe Clang when it comes to conforming stuff, but I wonder:
Which compiler is correct and what does the standard say about it?
#include <type_traits>
template<class T>
void check_error(void*, long);
template<class T>
struct foo{
template<class U>
friend typename std::enable_if<
std::is_same<T,U>::value
>::type check_error(foo<T>*, int){}
};
template struct foo<int>;
int main()
{
check_error<int>(static_cast<foo<int>*>(0), 0);
check_error<char>(static_cast<foo<char>*>(0), 0);
}
It is not the cast that instantiates the class template specialization, but the function call, because the argument triggers ADL . The instantiation is done because the completeness of it may affect the semantics of the program.
That clang does not follow the spec here is known and a PR was sent by me some time ago. See http://llvm.org/bugs/show_bug.cgi?id=9440
n3242 §14.7.1/1
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. The implicit instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or default arguments, of the class member functions,
member classes, static data members and member templates; and it causes the implicit instantiation
of the definitions of member anonymous unions. 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.
It seems to me that static_cast would require the instantiation of the declarations, but not of the definitions (as you are just dealing with pointers).
n3242 §14.6.5/1
Friend classes or functions can be declared within a class template. When a template is instantiated, the
names of its friends are treated as if the specialization had been explicitly declared at its point of instantiation.
I think that it should link, but maybe someone else can interpret better than I can.

ADL and friend injection

Consider this code:
template <int N>
struct X
{
friend void f(X *) {}
};
int main()
{
f((X<0> *)0); // Error?
}
compilers seem to heavily disagree. (MSVC08/10 says no, GCC<4.5 says yes, but 4.5 says no, sun 5.1 says yes, intel 11.1 says yes too but comeau says no (both are EDG)).
According to "C++ Templates - The complete guide":
... it is assumed that a call
involving a lookup for friends in
associated classes actually causes the
class to be instantiated ... Although
this was clearly intended by those who
wrote the C++ standard, it is not
clearly spelled out in the standard.
I couldn't find the relevant section in the standard. Any reference?
Consider this variation:
template <int N>
struct X
{
template <int M>
friend void f(X<M> *) {}
};
template <>
struct X<0>
{
};
int main()
{
X<1>();
f((X<0> *)0); // Error?
}
The key issue here is wether the viable function injected by X<1> should be visible during ADL for X<0>? Are they associated? All compilers mentioned above accept this code, except for Comeau which only accepts it in relaxed mode. Not sure what the standard has to say about this either.
What's your take on that?
The Standard says at 14.7.1/4
A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type affects the semantics of the program; in particular, if an expression whose type is a class template specialization is involved in overload resolution, pointer conversion, pointer to member conversion, the class template specialization is implicitly instantiated (3.2);
Note that Vandervoorde made an issue report here, and the committee found
The standard already specifies that this creates a point of instantiation.
For your second case - you need to consider the associated classes and namespaces of the argument f(X<0>*). These are, since this is a pointer to a class template specialization (note that "template-id" below is not quite correct - C++0x corrected that to use the correct term) and also a pointer to a class (this confusing split was also corrected in C++0x - it lists these two cases in one bullet point).
If T is a template-id, its associated namespaces and classes are the namespace in which the template is
defined; [... lots of noise ...]
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are defined.
So to summary, we have as associated classes are X<0> and the associated namespaces are the global namespace. Now the friend functions that are visible are
Any namespace-scope friend functions declared in associated classes are visible within their respective namespaces even if they are not visible during an ordinary lookup
There is no friend function declared in X<0> so the friend function declaration is not visible when looking into the global namespace. Note that X<0> is an entirely different class-type than X<1>. The implicit instantiation of X<1> you do there has no effect on this call - it just adds a non-visible name into the global namespace that refers to a friend function of class X<1>.