I have two test cases of a class having a static member instance. The first uses non-templated samples, while the second relies on generic object types.
The dilemma is simple: the constructor of the static member gets called before the main function (as it should), but only for the specific object types. The generic types do not exhibit the same behaviour. As the matter of fact, the constructor isn't compiled at all. It seems the compiler decided to completely overlook it as a means of (not completely justified) optimization.
I would like to know what is happening and what can be done to make it work in the most elegant way possible. I presume the obvious answer would be: use that static member somewhere in the code. I'd like not to do that, as the specific type case works without making use of that static member, apart from performing some "work" in its constructor.
The code samples:
//////////////////////////////////////////////
// Specific case
//////////////////////////////////////////////
class CPassive
{
public:
CPassive()
{
printf(" passively called ");
}
};
class CActive
{
private:
static CPassive ms_passive;
};
CPassive CActive::ms_passive;
///////////////////////////////////////////////////////////
// GENERIC TYPES
///////////////////////////////////////////////////////////
class CSample
{
public:
CSample()
{
printf("sample ");
}
};
template <typename T>
class CGenericPassive
{
public:
CGenericPassive()
{
T sample;
printf(" generic passive .. ");
}
private:
};
template <typename T>
class CGenericActive
{
private:
static CGenericPassive<T> ms_passive;
};
template<typename T>
CGenericPassive<T> CGenericActive<T>::ms_passive;
int main(int argc, char** argv)
{
CActive activeExample;// instantiates the static member
CGenericActive<CSample> activeExample; // obliterates the static from the class def.
}
Each (non-virtual) member of each class template you want instantiated needs to be referenced from non-template code, directly or indirectly. It is not enough to instantiate the class itself.
This is governed by the standard 14.7.1/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.
In your case it is enough to reference the member from CGenericActive constructor (you need to write this constructor obviously), like this:
CGenericActive()
{
// just reference it so it gets instantiated
(void)ms_passive;
}
Full live example.
Related
I didn't expect this to compile but it does. I'm wondering if this is guaranteed to happen or whether it's just compiler-specific. I'm wondering why 'OSInterface::handleOSEvents()' can see the 'InputHandler' class and its member function. If OSInterface is an ordinary class then it doesn't compile, the order of the class definitions must be swapped.
template <typename input_handler_t>
class OSInterface
{
public:
void readOSEvents()
{
input_handler_t::handleKeyDown(5);
}
};
struct InputHandler
{
static void handleKeyDown(int a) {};
};
int main()
{
OSInterface<InputHandler> os;
os.readOSEvents();
}
Edit: I was thinking the compiler must be doing something special with it, because if it instantiates the template class in the order that it's declared then it shouldn't be able to see 'InputHandler' and call its member function. On the other let's say that it instantiates the class right at the bottom of the .cpp then it would be able to see 'InputHandler' but I wouldn't be able to create an object of that class in main().
I'm wondering why 'OSInterface::handleOSEvents()' can see the 'InputHandler' class and its member function.
There is no OSInterface::handleOSEvents() in the example. I assume that you mean OSInterface::readOSEvents().
Because OSInterface is instantiated after InputHandler was defined.
I'm wondering if this is guaranteed to happen or whether it's just compiler-specific.
The program is well-formed. The program will compile with all standard conforming language implementations.
This question already has an answer here:
Why are some functions within my template class not getting compiled?
(1 answer)
Closed 2 years ago.
This C++ code builds successfully:
void h(int *)
{
}
template <typename T>
class A
{
public:
void f()
{
T *val;
h(val);
}
};
int main()
{
A<const int> a;
}
Why?
It is undeniable that A<const int>::f() cannot work.
If you call a.f(), it fails to build as expected.
Why is an instance of A<const int> even allowed to exist then?
I'm not sure how SFINAE applies here.
I'd appreciate a C++ standard quote or a relevant link.
It's allowed to exist because it can still be useful for it to exist.
Consider std::vector, which has a one-argument overload of resize, which default-constructs new elements. Definitely useful! But it's also useful to have a std::vector of some type which isn't default-constructible, when you don't intend to resize it in that way. Forcing all member functions to be available, even those not needed by the user, would artificially restrict the applicability of a class.
There's no sfinae here. If we look closely at the declaration of the function, and not it's implementation:
template <typename T>
class A
{
public:
void f();
};
You can see that there's nothing to tell the compiler that this function should not be part of overload resolution.
If we add a return type containing an expression, this is different:
template <typename T>
class A
{
public:
auto f() -> decltype(void(h(std::declval<T*>())));
};
That's different. Now The compiler need to resolve h(T*) to perform overload resolution. If the substitution fails (the instantiation of the function declaration) then the function is not part of the set.
As for why the code still compiles, take a look at this:
void h(int *)
{
}
template <typename T>
class A
{};
template<typename T>
void f()
{
T *val;
h(val);
}
int main()
{
A<const int> a;
}
It's undeniable that f(A<const int>) cannot work
If you call f(a), it fails to build as expected.
Why is an instance of A<const int> even allowed to exist then?
The answer should become obvious now: the function is never instantiated!
The very same applies for member functions. There is no use to instantiate all member function if you don't use them.
If you look for an official statement that comfirm this behavior, then look no further than the standard itself.
From [temp.inst]/4 (emphasis mine):
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.
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.
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;
}
};
I have a template class defined like so:
template <class T>
class Command {
public:
virtual T HandleSuccess(std::string response) = 0;
virtual std::string FullCommand() const = 0;
// ... other methods here ...
};
Will C++ allow me to create a non-template subclass of a template class? What I mean is can I do something like this:
class NoopCommand : public Command<NoopResult> {
public:
NoopResult HandleSuccess(std::string response);
std::string FullCommand() const;
// ... other methods here ...
};
As is that is not working for me because it says the following virtual functions are undefined:
T admix::Command<T>::HandleSuccess(std::string) [with T = admix::NoopResult]
std::string admix::Command<T>::FullCommand() const [with T = admix::NoopResult]
How can I specifically define them for the given T?
As we figured out in IRC, that was because you have
Made your functions non-pure
Sliced the derived object part. So the base class functions were called because the object wasn't a complete derived object anymore.
(Below follows my suspicion on earlier versions of your question - i keep it for further consideration and to keep the comments meaningful)
I think the issue here is that the compiler is free to instantiate any virtual function member of a class template even if it's not used (i.e not called). Instantiating a function will need a function definition to be provided. Try to add this in the header, where the compiler will find them and instantiate a definition from:
template<typename T>
T Command<T>::HandleSuccess(std::string response) { /* put some default action ... */ }
template<typename T>
std::string Command<T>::FullCommand() const { /* put some default action ... */ }
C++ Standard 14.7.1/9:
An implementation shall not implicitly instantiate a function template, a member template, a non-virtual
member function, a member class or a static data member of a class template that does not require instantiation. It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated.
"virtual functions are undefined" means you have not defined the function bodies of NoopCommand::HandleSuccess and NoopCommand::FullCommand.
The following should solve your problem.
class NoopCommand : public Command<NoopResult> {
public:
NoopResult HandleSuccess(std::string response) {}
std::string FullCommand() const {}
// ... other methods here ...
};
Or you you have a NoopCommand.cpp, make sure it's included in your build process.
The pattern you use is widely known as the "Curiously Recurring Template Pattern". so, yes, you can do that.
I can't think of a reason why it does not compile.
The code you gave compiles for me, without errors (after adding a struct NoopResult { };). Maybe there's a problem in the code you left out?
litb found the solution on ##c++ last night.
The issue was I was passing a NoopCommand to a function like this:
void SendCommand(Command<T> command);
When I should have made the signature this:
void SendCommand(Command<T>& command);
Making that change allows everything to compile.