关于使用 Singleton CRTP 的模板参数类型的内联静态成员,哪个编译器是正确的?

Which compiler is right about inline static members of template parameter type using a Singleton CRTP?

我制作了一个非常简单的 Singleton 基础 class,它可以与 CRTP 一起使用,使任何从它派生的 class 成为单例。不过我发现了一些奇怪的事情。不同的编译器不同意代码是否格式错误。在这里:

template <typename Derived>
class Singleton
{
public:
    inline static Derived instance;
};

正如我所说,它非常简单,但足以用于演示目的。此处显示了一个示例用法:

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_) }
    {
    }
};

这段代码在 GCC, and Clang. However MSVC 下编译通过说 MyClass 没有提供适当的默认构造函数来拒绝它(尽管它显然被声明为 default)。出于好奇,我尝试使用不同的构造函数:

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_) }
    {
    }
};

这再次被 GCC and Clang, but rejected by MSVC 接受,它声称这次 MyClass 未定义。

问题是:这段代码格式不正确吗?如果是,为什么? 因此,哪些编译器是正确的? GCC 和 Clang 只是利用“不需要诊断”的优势吗?

我的直觉是 MSVC 是错误的,因为通常是这种情况,尽管另一方面,它通常比 GCC 和 Clang 更宽松,所以我很好奇答案。

GCC 和 Clang 是正确的。

temp.inst/3:

The implicit instantiation of a class template specialization causes

  • the implicit instantiation of the declarations, but not of the definitions, of the non-deleted class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; and
  • the implicit instantiation of the definitions of deleted member functions, unscoped member enumerations, and member anonymous unions.

Singleton<Derived> 实例化的上下文中,Derived 确实是不完整的,但这并不重要,因为可以用不完整的类型声明。

另请参阅:gcc bug #71534