Ensuring a static member of a class is constructed - c++

Consider the following code
#include <iostream>
using namespace std;
struct Printer{
Printer(){
std::cout << "Created\n";
}
};
template<class Derived>
struct InitPrinter{
static Printer p;
};
template<class Derived>
Printer InitPrinter<Derived>::p;
struct MyClass:InitPrinter<MyClass>{
MyClass(){}
};
// Uncomment line below to print out created
//auto& p = MyClass::p;
int main() {
return 0;
}
I expected that this would print out "Created", however, it does not print out anything (tested with MSVC and with ideone gcc c++11). Is this a compiler implementation issue, or is this behavior supported by the standard? If the commented out line is uncommented then it prints out as expected. Is there any way to the static Printer p to be instantiated without requiring either changes to MyClass or extra statements like the auto& p = MyClass::p?
The reason I am interested in this is I am looking to have create a templated base class, that will run some code at startup when it is derived from.

The appropriate quote is [temp.inst]/2
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.
emphasis mine.
There's also [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, scoped member enumerations, static data members and member templates [...]
and [temp.inst]/10
An implementation shall not implicitly instantiate a function template, [...] or a static data member of a class template that does not require instantiation.

Related

C++ class template function can access nested class private member

The following code fails to compile as expected:
#include<iostream>
class Enclosing {
int x;
class Nested { int y; };
void EnclosingFun(Nested *n) {
std::cout << n->y; // Compiler Error: y is private in Nested
}
};
However, if I change EnclosingFun into a template member function, the compiler (gcc-7) doesn't complain about accessing y:
#include<iostream>
class Enclosing {
int x;
class Nested { int y; };
template <typename T>
void EnclosingFun(Nested *n, T t) {
std::cout << t << n->y; // OK? Why?
}
};
Is this a bug in gcc? Or does c++ have different access rules for template member function to access nested classes?
Is this a bug in gcc? Or does c++ have different access rules for template member function to access nested classes?
No either.
According to the standard, §17.8.1 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.
That means, Enclosing::EnclosingFun() is not instantiated here. Adding the invocation to it would cause it to be instantiated, then you'll get the error, e.g.
prog.cc:8:30: error: 'int Enclosing::Nested::y' is private within this context
std::cout << t << n->y; // OK? Why?
~~~^
LIVE
It is not a bug in GCC. You do not call your function template, therefore the function is not instantiated, and it does not try to access the private member of the nested class. Try to call your function somewhere in a real function (not template) or try to refer it (implicit instantiation).
A function template by itself is not a type, or a function, or any other entity. No code is generated from a source file that contains only template definitions. In order for any code to appear, a template must be instantiated: the template arguments must be determined so that the compiler can generate an actual function (or class, from a class template).
When code refers to a function in context that requires the function definition to exist, and this particular function has not been explicitly instantiated, implicit instantiation occurs.
template void Enclosing::EnclosingFun(Enclosing::Nested *n, int t); // instantiates the function for the second int parameter

Creating explicitly specialized template class object yields "object has initializer but incomplete type" error

Simple case. I don't quite understand why the parentheses are necessary for calling the default ctor of the explicitly instantiated template.
And, why calling the non-default ctor of the explicitly instantiated template gives me the "incomplete type" error?
Thank you very much!
// X.h
template <const int MODE>
class X{
public:
X() = default;
X(int& a) {++a;}
// define X here
};
// declare the explicit specialization
template <> class X<1>;
// Then define the default behaviors of X.
// X.cpp
#include "X.h"
template <>
class X<1>{
public:
X() = default;
X(int& a) {--a;}
// define X<1>
};
// Then specialize behavior.
// main.cpp
#include "X.h"
int main(){
X<2> x_2; // fine, instantiates the default version
X<1> x_1(); // Edit: A function declaration, as pointed out in the comment.
X<1> x_1_1; // error: aggregate ‘X<1> x_1_1’ has incomplete type and cannot be defined
int a = 0;
X<1> x_1_2(a); // error: variable ‘X<1> x_1_2’ has initializer but incomplete type
}
As pointed out by others X<1> x_1(); is only a function declaration, so it doesn't actually instantiate an object of type X<1>. For the incomplete type error: You have to put the whole declaration of X<1> into the header file (not only a forward declaration, as you did now). You can put the implementation in the cpp file, but anyone using objects (and not only pointers to objects) of type X<1> (in this case: main) has to know how large it is and what methods it provides.
Your confusion might in part stem from the way you use specialisation in your example: In your specialisation, the only thing that differs from the general template is the definition of one of the constructors (all the signatures stay the same). So you might have thought the compiler could figure this out by itself. In fact, it cannot possibly do that, because your specialised class might look completely different (with different constructors, different members/member functions) from the unspecialised template. Like this:
template <int I>
struct X {
bool hello(int x, int y);
};
template<>
struct X<1> {
int goodbye(std::string x);
};
If the compiler only sees template<> struct X<1>; instead, how should it figure out the interface of this class?
template <> class X<1>; is just a forward declaration of the specialization, and conveys no information about the layout of the type. Since the actual specialization is not visible from main.cpp (it is defined in X.cpp), the type is indeed incomplete.
Keep in mind that class template specializations share nothing with the template class other than its base name, so the compiler has no idea how many bytes to allocate on the stack for each instance (nor whether the requested constructor even exists!) unless it knows the definition of the specialization, which you have hidden away in a .cpp file.
This is akin to doing class Foo; and then trying to declare a variable of type Foo without providing a definition of the type.

decltype in class method declaration: error when used before "referenced" member is declared

Consider the following code:
struct test {
auto func() -> decltype(data) {} // ERROR
int data;
};
int main() {
test t;
t.func();
}
It gives the following error:
main.cpp:2:29: error: 'data' was not declared in this scope
auto func() -> decltype(data) {}
However, if I place data above func(), it gives out no error (live code):
struct test {
int data;
auto func() -> decltype(data) {}
};
...
And so my question is, why is decltype not considering members declared after it (when decltype is used in a method declaration, not in the definition)? I also want to know if there are any change in this behavior in future iterations of the language standard.
Please note that I'm asking this because I was expecting decltype to behave differently. My coding convention is to place class data members below the class functions. Surely this different behavior would affect how I organize my class members. I would be very grateful if you can provide any workaround that would preserve my coding convention.
The trailing return type is part of the member function declaration, which does not have access to data members or member functions declared after it, unlike the member function definition, which does. I am not aware of any change in this behaviour in C++14.
See 3.4.1-7 of the C++11 standard, Unqualified name look-up:
A name used in the definition of a class X outside of a member function body or nested class definition
shall be declared in one of the following ways:
before its use in class X or be a member of a base class of X (10.2), or...
(emphasis mine)

C++ templates and static members - definition in the header

Consider the following construct:
//! Templated singleton.
/*!
Template wrapper enforcing the singleton behavior.
*/
template <class T>
class TSingleton
{
private:
//! Singleton instance pointer.
static T* instance;
//! private constructor.
TSingleton() { }
//! private empty copy constructor.
TSingleton(const TSingleton<T>& sourceObject) {}
public:
//! Static singleton instance getter.
static T* GetInstance()
{
if (instance == 0)
instance = new T();
return instance;
}
};
template <class T> T* TSingleton<T>::instance = 0;
This template class and the definition of the static instance are written in the same header file. For a non-template class, this causes a link-time error due to multiple symbols being defined for the instance static member. It seems intuitive for this to happen with templates as well, thus one must separate the definition and put it in a .cpp file. But templates are usually declared and defined in header-like files. What is it that allows this syntax to be valid and functional for template classes?
There a wikipedia link on this, but it does not provide a clear explanation on what happens in case of template classes.
This works because [basic.def.odr]/5 explicitly allowed templates to be duplicated:
There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. ...
The requirements are quite lengthy, so I won't duplicate them here, but essentially they state that each duplicate definition must be identical (otherwise the program has undefined behaviour).

Initializing static pointer in templated class

Consider a class like so:
template < class T >
class MyClass
{
private:
static T staticObject;
static T * staticPointerObject;
};
...
template < class T >
T MyClass<T>::staticObject; // <-- works
...
template < class T >
T * MyClass<T>::staticPointerObject = NULL; // <-- cannot find symbol staticPointerObject.
I am having trouble figuring out why I cannot successfully create that pointer object.
The above code is all specified in the header, and the issue I mentioned is an error in the link step, so it is not finding the specific symbol.
"Cannot find symbol staticPointerObject" - this looks like a linker error message. Is it? (Details like this have to be specified in your question).
If it is, them most likely it happens because you put the definition(s) of your static member(s) into an implementation file (a .cpp file). In order for this to work correctly, the definitions should be placed into the header file (.h file).
Again, details like this have to be specified in your question. Without them it turns into a random guess-fest.
I suspect the reason your first example is the following (from the 2003 C++ std document). Note particularly the last sentence -- from your example, there seems to be nothing "that requires the member definition to exist".
14.7.1 Implicit instantiation [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 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.
Your first "definition" of static member is but a declaration - here is a quote from the standard.
15 An explicit specialization of a
static data member of a template is a
definition if the declaration includes
an initializer; otherwise, it is a
declaration. [Note: there is no syntax
for the definition of a static data
member of a template that requires
default initialization. template<> X
Q::x; This is a declaration
regardless of whether X can be default
initialized (8.5). ]
The second definition should work. Are you sure you have everything available in one compilation unit? What is teh exact text of error message?
The following compiles/runs with g++ - all in one file
#include <iostream>
template < class T >
class MyClass
{
public:
static T staticObject;
static T * staticPointerObject;
};
template < class T >
T MyClass<T>::staticObject;
template < class T >
T * MyClass<T>::staticPointerObject = 0;
int main(int argc, char **argv)
{
int an_int = 5;
MyClass<int>::staticPointerObject = &an_int;
std::cout << *MyClass<int>::staticPointerObject << std::endl;
char a_char = 'a';
MyClass<char>::staticPointerObject = &a_char;
std::cout << *MyClass<char>::staticPointerObject << std::endl;
}
I have found two solutions. Neither of them are 100% what I was hoping for.
Explicitely initialize the specific instance, e.g.
int * MyClass<int>::staticPointerObject = NULL;
This is not convinient especially when I have a lot of different types.
Wrap the pointer inside the class, e.g.
template < class T >
class MyClass
{
private:
struct PointerWrapper
{
T * pointer;
PointerWrapper( void )
: pointer( NULL )
{ }
};
T staticObject;
PointerWrapper staticPointerObject;
};
...
template < class T >
T MyClass<T>::staticObject; // <-- works fine.
...
template < class T >
MyClass<T>::PointerWrapper MyClass<T>::staticPointerObject; // <-- works fine.
This is a bit of a hassle, but at least usable. Why is it I can instansiate a variable object but not a pointer to a variable object? If anything I would think I'd have more problems the other way around (the compiler knows ahead of time what a pointer looks like, but not what my object looks like).
If someone has a better answer I'd love to see it!
I use the following trick all the time. The idea is to put your static in a function, and access it only from that function. This approach also allows you to avoid the need to declare your static in a .cpp file -- everything can live in the .h file. Following your example code:
template < class T >
class MyClass
{
public:
static T * getObject() {
// Initialization goes here.
static T * object = NULL; // or whatever you want
return pointerObject;
}
};