【问题标题】:Which compiler is right about inline static members of template parameter type using a Singleton CRTP?哪个编译器对使用单例 CRTP 的模板参数类型的内联静态成员是正确的?
【发布时间】:2020-11-28 12:17:09
【问题描述】:

我制作了一个非常简单的Singleton 基类,它可以与 CRTP 一起使用,以使任何派生自它的类成为单例。不过我发现了一些奇怪的东西。不同的编译器对代码是否格式错误存在分歧。这里是:

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

此代码在GCCClang 下编译。然而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_) }
    {
    }
};

这再次被GCCClang 接受,但被MSVC 拒绝,这一次声称MyClass 未定义。

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

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

【问题讨论】:

  • Derived 是 CRTP 中的不完整类型。

标签: c++ templates singleton language-lawyer crtp


【解决方案1】:

GCC 和 Clang 是正确的。

temp.inst/3:

类模板特化的隐式实例化原因

  • 未删除的类成员函数、成员类、范围成员枚举、静态数据成员声明而非定义的隐式实例化,会员模板和朋友;和
  • 已删除成员函数、无范围成员枚举和成员匿名联合的定义的隐式实例化。

Singleton&lt;Derived&gt; 实例化的上下文中,Derived 确实是不完整的,但这并不重要,因为可以使用不完整的类型进行声明。

另见:gcc bug #71534

【讨论】:

  • 不会也定义instance,因为它被标记为inline
  • 是的,该声明既是声明又是定义。问题是,静态数据成员的 declaration 何时实例化,以及该静态数据成员的 definition 何时实例化?它们不是同一个答案,即使两个事件都来自同一个陈述。在 Singleton&lt;Derived&gt; 的隐式实例化中,Derived 是不完整的,所以如果我们必须实例化 定义,我们会遇到真正的问题,但如果我们只实例化 declaration.
  • 标准规定我们只实例化声明。除非需要,否则不会实例化静态数据成员的定义(另请参见 temp.inst/4:“特别是,除非静态数据成员是本身的使用方式需要静态数据成员的定义存在")
  • 在需要instance时(在main中),编译器实例化定义,这当然没问题,因为Derived是完整的。
  • 感谢您的解释。不过还有一件事让我很困扰。出于某种原因,尽管这与用户定义的类型一起使用,但据我所知,将其与标准库一起使用会导致 UB。来自 [res.on.functions]:“2. 特别是,在以下情况下效果未定义:[...] 2.5 如果在实例化一个不完整类型 ([basic.types]) 时将其用作模板参数模板组件或评估概念,除非该组件特别允许。”。是我理解错了还是有这种不一致的原因?
猜你喜欢
  • 2018-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多