【问题标题】:Static templated constexpr nested class member静态模板化 constexpr 嵌套类成员
【发布时间】:2017-01-15 19:18:15
【问题描述】:

我有以下示例类Foo 和嵌套类Bar,一切都是constexpr

class Foo
{
private:
    template <typename T>
    struct Bar
    {
        constexpr Bar(){}
        constexpr int DoTheThing() const
        {
            return 1;
        }
    };
        
public:
    constexpr static auto b = Bar<int>{};
    constexpr Foo() {}
    constexpr int DoTheThing() const
    {
        return b.DoTheThing();
    }
};

而且我想测试调用Foo::DoTheThing 返回1:

int main()
{
   constexpr Foo f;
   static_assert(f.DoTheThing() == 1, "DoTheThing() should return 1");
}

GCC 和 Clang 都在这里抱怨,但 MSVC 没有

GCC 说:

错误:constexpr Foo::Bar&lt;T&gt;::Bar() [with T = int] 在其定义之前使用

constexpr static auto b = Bar<int>{};

还有Clang

错误:constexpr 变量b 必须由常量表达式初始化

constexpr static auto b = Bar<int>{};

我无法判断标准是否不允许这样做,但我的猜测是 b 不知何故是一个不完整的类型。

让事情变得更有趣的是,如果我删除 constexpr,或者如果我将 Bar 的定义移到 Foo 之外,我可以让 GCC 和 Clang 正常运行。

哪些编译器是正确的?

请注意,此问题的灵感来自以下内容:

【问题讨论】:

  • Bar 移到Foo 之外的事实向我表明,这里的一个因素是Foo 的定义在类被完全声明之前是不完整的。直到 Foo 的定义结束,它的内部类才被认为是完全定义的(这就是为什么内联 clsas 方法可以引用在它们之后声明的类成员)。
  • Bar 已经是literal type,因此您无需定义默认构造函数(在此示例中)。 Clang 和 GCC compile fine 如果您取出 Bar 的构造函数。我知道这不能回答你的问题,但我认为值得一提。
  • @SamVarshavchik 我最初倾向于同意你的观点,只是简单地从b 中删除constexpr static 允许everything to work just fine

标签: c++ templates c++14 language-lawyer constexpr


【解决方案1】:

来自 n4140

§ 9.2.2 [class.mem](强调我的)

一个类被认为是一个完全定义的对象类型(3.9)(或 完整类型)在类说明符的结束}。内 class member-specification,该类被认为是完整的 函数体、默认参数、使用声明 继承构造函数 (12.9)、exception-specificationsbrace-or-equal-initializers 用于非静态数据成员(包括 嵌套类中的此类内容)。 否则视为不完整 在它自己的类中成员规范

Clang 和 GCC 是正确的。当您声明 static constexpr 成员时,该类不被认为是完整的,因此您无法构造它。这就是为什么将 Bar 的定义移出或删除 static constexpr 的原因(因为在定义非静态成员时它被认为是完整的)


澄清一下,特别是考虑到这个问题:Static constexpr member of an inner class

我上面引用的标准语言基本上意味着除非另有说明一个类在其自身内部被认为是不完整的*staticconstexprstatic constexpr 初始化器不属于 otherwise specified 部分,因此我们不能使用在类中声明的 anything,这包括嵌套类类型。

*意味着你不能在类声明中使用它或它的成员。最著名的例外是在成员函数中。

【讨论】:

  • 嗯...但是我怎么能这样做:constexpr static auto b = sizeof(Bar&lt;int&gt;);您的回答的第二部分基本上指出外部类型的不完整性也意味着嵌套类型的不完整性(即使后者“似乎”是完整的)。但是sizeof 也需要一个 complete 类型作为它的参数。这个嵌套类型怎么编译?
  • @AnT:我更相信这里的标准没有明确规定。如果我默认嵌套类的constexpr 构造函数,那么它将起作用。这是有道理的,因为编译器可以保证构造函数不会尝试访问外部类的任何方面。否则,您可以引用例如外部类的 static constexpr 成员,这会使事情复杂化。我说该标准“未充分说明”,因为我认为基于它的编写方式,OP 中的代码应该是格式良好的,但实施者认为,如果他们允许,就无法做出真正的保证。
  • @AnT: 具体来说,我的意思是this case,其中嵌套类指的是父类的static constexpr 成员(合法),但父类的static constexpr 成员是嵌套类的实例.一般来说,在最外层的类完成之前,编译器不能允许构造(甚至static constexpr)嵌套类,除非它可以保证不会发生某种循环引用(这很容易用默认的ctor来保证)
  • 除了完整性问题之外,如果constexpr成员函数的类不完整,是否可以在常量表达式中使用也存在问题。请参阅核心语言问题 12551626
猜你喜欢
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 2023-03-31
  • 2017-07-02
  • 1970-01-01
相关资源
最近更新 更多