I am trying to find an easy way (with pre C++11, ie no decltype) to document what requirements a template has on a type to work properly. Maybe there are better ways to do this. However, this is my question:
#include <iostream>
template <typename T> struct Foo {
static const int test = sizeof(T::size);
};
template <typename T> struct DetectAndError {
DetectAndError() { int test = sizeof(T::size); }
};
struct Bar {};
int main() {
Foo<Bar> x; // NO ERROR ? :/
// std::cout << x.test << std::endl; // ERROR :)
// DetectAndError<Bar> y; // ERROR :)
}
Why is Foo<Bar> x; not an error?
For the other lines I get what I want:
error: 'size' is not a member of 'Bar'
This is because the standard mandates that test will be instantiated only when it is used. Member variables/member functions/static members of a template class aren't instantiated without them being used.
In your case the moment you try to do a x.test compiler tries to find test and subsequently cannot do it as x::size is missing.
The behaviour is pretty much accepted and common and ofcourse as per the standard.
The other answer is valid, but here's some standardese for it:
N4140 § 14.7.1 [temp.inst]/ 1 and 2
The implicit instantiation of a class template specialization causes
the implicit instantiation of the declarations, but not of the
definitions, default arguments, or exception-specification s 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 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.
Thus, test is only declared in the first line, but yields an error when instantiation is attempted. As for DetectAndError, you're implicitly calling its default constructor. typedef DetectAndError<Foo> foo or DetectAndError<Foo>* ptr should compile with no problems
Related
Consider the following code:
#include <iostream>
struct M {
M() { std::cout << "M\n"; }
};
template <class T>
struct Test {
Test() { std::cout << "Test\n"; }
inline static auto m = M{};
};
int main() {
Test<int> t1;
//Test t;
//(void)&t1.m;
}
Using the latest GCC or Clang the only "Test" is printed out. But if we use an address of m object (uncomment the last line (void)&t1.m;) or transform Test class template into the regular (non-templated) class then the M constructor was called.
Is this behaviour allowed by the C++ standard? Any quotes?
Yes, it's spelled out in the standard.
[temp.inst] (emphasis mine)
4 Unless a member of a class template or a member template is a
declared specialization, the specialization of the member is
implicitly instantiated when the specialization is referenced in a
context that requires the member definition to exist or if the
existence of the definition of the member affects the semantics of the
program; 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.
Since your example doesn't use the static data member at all, its definition is never fully instantiated.
I am trying to understand the following code snippets
Snippet #1
template <typename T>
struct A
{
static constexpr int VB = T::VD;
};
struct B : A<B>
{
};
Neither gcc9 nor clang9 throw an error here.
Q. Why does this code compile? Aren't we instantiating A<B> when inheriting from B? There is no VD in B, so shouldn't the compiler throw an error here?
Snippet #2
template <typename T>
struct A
{
static constexpr auto AB = T::AD; // <- No member named AD in B
};
struct B : A<B>
{
static constexpr auto AD = 0xD;
};
In this case, gcc9 compiles fine but clang9 throws an error saying "No member named AD in B".
Q. Why does it compile with gcc9/why doesn't it compile with clang9?
Snippet #3
template <typename T>
struct A
{
using TB = typename T::TD;
};
struct B : A<B>
{
using TD = int;
};
Here both clang9 and gcc9 throw an error. gcc9 says "invalid use of incomplete type 'struct B'".
Q. If struct B is incomplete here then why is it not incomplete in snippet #2?
Compiler flags used: -std=c++17 -O3 -Wall -Werror. Thanks in Advance!!!
I believe these essentially boil down to [temp.inst]/2 (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 noexcept-specifiers of the class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; […]
and [temp.inst]/9
An implementation shall not implicitly instantiate […] a static data member of a class template […] unless such instantiation is required.
The wording in the standard concerning implicit template instantiation leaves many details open to interpretation. In general, it would seem to me that you simply cannot rely on parts of a template not being instantiated unless the specification explicitly says so. Thus:
Snippet #1
Q. Why does this code compile? Aren't we instantiating A when inheriting from B? There is no VD in B, so shouldn't the compiler throw an error here?
You are instantiating A<B>. But instantiating A<B> only instantiates the declarations, not the definitions of its static data members. VB is never used in a way that would require a definition to exist. The compiler should accept this code.
Snippet #2
Q. Why does it compile with gcc9/why doesn't it compile with clang9?
As pointed out by Jarod42, the declaration of AB contains a placeholder type. It would seem to me that the wording of the standard is not really clear on what is supposed to happen here. Does the instantiation of the declaration of a static data member that contains a placeholder type trigger placeholder type deduction and, thus, constitute a use that requires the definition of the static data member? I can't find wording in the standard that would clearly say either yes or no to that. Thus, I would say that both interpretations are equally valid here and, thus, GCC and clang are both right…
Snippet #3
Q. If struct B is incomplete here then why is it not incomplete in snippet #2?
A class type is only complete at the point where you reach the closing } of the class-specifier [class.mem]/6. Thus, B is incomplete during the implicit instantiation of A<B> in all your snippets. It's just that this was irrelevant for Snippet #1. In Snippet #2, clang did give you an error No member named AD in B as a result. Similar to the case of Snippet #2, I can't find wording on when exactly member alias declarations would be instantiated. However, unlike for the definition of static data members, there is no wording in place to explicitly prevent the instantiation of member alias declarations during implicit instantiation of a class template. Thus, I would say that the behavior of both GCC and clang is a valid interpretation of the standard in this case…
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.
struct Value {
using a_type = int;
a_type f() { return 1; }
};
template<typename T>
struct Wrapper {
T t;
auto call_f() { return t.f(); }
};
int main() {
Wrapper<Value> w;
Wrapper<int> w2;
w.call_f();
}
This compiles fine on Clang and GCC. Wrapper<int> gets instantiated even though the return type of Wrapper<int>::call_f() can not be deduced (there is no int::f()). It fails only when w2.call_f() gets called.
Is this part of the C++ standard, and can it be expected to work on all compilers?
Yes, this is part of the C++ standard.
The rules of template instantiation are long and complex, but the short version is that a member function of a template class is only instantiated when needed. If nothing calls it, or tries to take a pointer to it, or explicitly instantiates it (and probably a few other cases that I'm forgetting), then it won't be instantiated and your code is well-formed.
As #dyp points out, it is only the declaration of the member function which is instantiated when the class definition is instantiated ([temp.inst]/1), but the return type deduction is only carried out when the function definition is instantiated ([dcl.spec.auto]/12).
This is extremely helpful both for minimising the overhead of templates and being permissive about type requirements. It's this feature which lets you do something like this:
struct Foo {
//no default constructor
Foo(int);
};
std::vector<Foo> foos;
Some std::vector functions (resize, for example) require T to be default-constructible, but as long as you don't call those functions you can still use other features of std::vector.
Yes, it's guaranteed by the standard. Wrapper<T>::call_f() will be implicitly instantiated only when it gets called.
$14.7.1/2 Implicit instantiation
[temp.inst]:
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;
$14.7.1/8 Implicit instantiation
[temp.inst]:
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 ([stmt.if]), unless such instantiation is required.
Consider this code:
struct A; // incomplete type
template<class T>
struct D { T d; };
template <class T>
struct B { int * p = nullptr; };
int main() {
B<D<A>> u, v;
u = v; // doesn't compile; complain that D<A>::d has incomplete type
u.operator=(v); // compiles
}
Demo. Since u.operator=(v) compiles but u = v; doesn't, somewhere during the overload resolution for the latter expression the compiler must have implicitly instantiated D<A> - but I don't see why that instantiation is required.
To make things more interesting, this code compiles:
struct A; // incomplete type
template<class T>
struct D; // undefined
template <class T>
struct B { int * p = nullptr; };
int main() {
B<D<A>> u, v;
u = v;
u.operator=(v);
}
Demo.
What's going on here? Why does u = v; cause the implicit instantiation of D<A> - a type that's nowhere used in the body of B's definition - in the first case but not the second?
The entire point of the matter is ADL kicking in:
N3797 - [basic.lookup.argdep]
When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered
during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope
friend function or function template declarations (11.3) not otherwise visible may be found.
following:
For each argument type T in the function call, there is a set of zero or more associated namespaces and a
set of zero or more associated classes to be considered. [...] The sets of
namespaces and classes are determined in the following way:
If T is a class type [..] its associated classes are: ...
furthemore if T is a class template specialization its associated namespaces and classes also include: the namespaces and classes associated with the
types of the template arguments provided for template type parameters
D<A> is an associated class and therefore in the list waiting for its turn.
Now for the interesting part [temp.inst]/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 completeness of the class type affects the semantics of the program
One could think that the completeness of the type D<A> doesn't affect at all the semantic of that program, however [basic.lookup.argdep]/4 says:
When considering an associated namespace, the lookup is the same as the lookup performed when the associated namespace is used as a qualifier (3.4.3.2)
except that:
[...]
Any namespace-scope friend functions or friend function templates declared in associated classes are visible within their respective
namespaces even if they are not visible during an ordinary lookup (11.3)
i.e. the completeness of the class type actually affects friends declarations -> the completeness of the class type therefore affects the semantics of the program.
That is also the reason why your second sample works.
TL;DR D<A> is instantiated.
The last interesting point regards why ADL starts in the first place for
u = v; // Triggers ADL
u.operator=(v); // Doesn't trigger ADL
§13.3.1.2/2 dictates that there can be no non-member operator= (other than the built-in ones). Join this to [over.match.oper]/2:
The set of non-member candidates is the result of the unqualified lookup of operator# in the context
of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2)
except that all member functions are ignored.
and the logical conclusion is: there's no point in performing the ADL lookup if there's no non-member form in table 11. However [temp.inst]p7 relaxes this:
If the overload resolution process can determine the correct function to call without instantiating a class template definition, it is unspecified whether that instantiation actually takes place.
and that's the reason why clang triggered the entire ADL -> implicit instantiation process in the first place.
Starting from r218330 (at the time of writing this, it has been committed a few minutes ago) this behavior was changed not to perform ADL for operator= at all.
References
r218330
clang sources / Sema module
cfe-dev
N3797
Thanks to Richard Smith and David Blaikie for helping me figuring this out.
Well, I think in Visual Studio 2013 code should look like(without = nullptr):
struct A; // incomplete type
template<class T>
struct D { T d; };
template <class T>
struct B { int * p; };
int void_main() {
B<D<A>> u, v;
u = v; // compiles
u.operator=(v); // compiles
return 0;
}
In this case it should compile well just because incomplete types can be used for specific template class specialization usage.
As for the run-time error - the variable v used without initialization - it's correct - struct B does not have any constructor => B::p is not initialized and could contain garbage.