The constructor order problem
I have probably faced a bug in gcc compiler when I was solving a problem of making an arbitrary trait of a class like ThorsSerializer does, but more generic. Moreover I am not sure how to report such an error. I have tried following gcc versions 7.6, 8.2, 9.2. Here is the minimal code presenting the problem I have ran into:
#include <iostream>
#include <string>
struct Member{
std::string s;
Member(){std::cout <<"Member constructor: "<< s << std::endl;}
};
template<typename StatMemberType>
class Message {
public:
static StatMemberType s;
Message(const std::string& message){
s.s = message;
std::cout << "Message constructor s.s: "<< s.s << std::endl;
}
static std::string& getMessage() {return s.s;}
};
template <typename StatMemberType> StatMemberType Message<StatMemberType>::s;
/**
The class MessageHello creates a global instance of Message saying Hello
*/
template <typename dummy>
class MessageHello {
public:
static Message<Member> message;
MessageHello() {std::cout << "MessageHello constructor message.getMessage(): "<< message.getMessage() << std::endl;}
};
template <typename dummy> Message<Member> MessageHello<dummy>::message("Hello");
void testConstrucorOrder() {
std::cout << "global value of MessageHello: "<< MessageHello<void>::message.getMessage() << std::endl;
}
int main()
{
testConstrucorOrder();
std::cout << "\nthe expected output:\n";
std::cout << "Member constructor:\nMessage constructor s.s: Hello"<< std::endl;
std::cout << "global value of MessageHello: Hello";
}
The output of this code using gcc compiler is:
Message constructor s.s: Hello
Member constructor:
global value of MessageHello:
But I wanted to obtain this: (When I try to compile this example with clang 5.0, the obtained result is as desired.)
Member constructor:
Message constructor s.s: Hello
global value of MessageHello: Hello
The incorrect order of constructing the elements causes resetting of the attribute Member
after the value was set in constructor of class Message
. Thus, the global value of MessageHello
is empty.
My solution
I would like to ask whether my solution using Singleton class to preserve the correct order of constructors is ok? The solution reads:
#include <iostream>
#include <string>
#include <memory>
/**
* @brief The Singleton class
*/
template<class Class>
class Singleton {
public:
static Class& getInstance(){
if (p == nullptr) {
p = std::unique_ptr<Class>(new Class); // the class must be trivially constructible
}
return *p;
}
protected:
static std::unique_ptr<Class> p;
Singleton() {}
private:
// disable move and copy options
Singleton(Singleton const&) = delete;
Singleton(Singleton const&&) = delete;
Singleton& operator=(Singleton const&) = delete;
Singleton& operator=(Singleton const&&) = delete;
};
template <class Class> std::unique_ptr<Class> Singleton<Class>::p = nullptr;
struct Member{
std::string s;
Member(){std::cout <<"Member constructor: "<< s << std::endl;}
};
template<typename StatMemberType>
class Message {
public:
Message(const std::string& message){
Singleton<StatMemberType>::getInstance().s = message;
std::cout << "Message constructor Singleton<StatMemberType>::getInstance().s: "<< getMessage() << std::endl;
}
static std::string& getMessage() {return Singleton<StatMemberType>::getInstance().s;}
};
/**
The class MessageHello creates a global instance of Message saying Hello
*/
template <typename dummy>
class MessageHello {
public:
static Message<Member> message;
MessageHello() {std::cout << "MessageHello constructor message.getMessage(): "<< message.getMessage() << std::endl;}
};
template <typename dummy> Message<Member> MessageHello<dummy>::message("Hello");
void testConstrucorOrder() {
std::cout << "global value of MessageHello: "<< MessageHello<void>::message.getMessage() << std::endl;
}
int main()
{
testConstrucorOrder();
}
This code works correctly even when compiled by gcc.
Member constructor:
Message constructor Singleton<StatMemberType>::getInstance().s: Hello
global value of MessageHello: Hello