I made a very simple Singleton base class that can be used with CRTP to make any class that derives from it a singleton. I found something strange though. Different compilers disagree on whether the code is ill-formed or not. Here it is:
template <typename Derived>
class Singleton
{
public:
inline static Derived instance;
};
As I said, it is very simple, but that is enough for demonstration purposes. An example usage is shown here:
class MyClass : public Singleton<MyClass>
{
public:
std::string msg;
MyClass() = default;
MyClass(const std::string& msg_) : msg{ msg_ }
{
}
MyClass(std::string&& msg_) noexcept : msg{ std::move(msg_) }
{
}
};
This code compiles under both GCC, and Clang. However MSVC rejects it by saying that MyClass provides no appropriate default constructor (despite it obviously being declared as default). Out of curiosity I tried using a different constructor:
template <typename Derived>
class Singleton
{
public:
inline static Derived instance{"hello MSVC"};
};
class MyClass : public Singleton<MyClass>
{
public:
std::string msg;
MyClass() = default;
MyClass(const std::string& msg_) : msg{ msg_ }
{
}
MyClass(std::string&& msg_) noexcept : msg{ std::move(msg_) }
{
}
};
This is again accepted by GCC and Clang, but rejected by MSVC, which claims this time that MyClass is undefined.
The question is: Is this code ill-formed? If it is, why? And consequently, which compilers are right? Are GCC and Clang simply taking advantage of "no diagnostic required"?
My gut feeling is that MSVC is wrong, as this is usually the case, though on the other hand, it is usually more permissive than GCC and Clang, so I am curious of the answer.