当前位置: 首页 > 知识库问答 >
问题:

静态模板化constexpr嵌套类成员

钮边浩
2023-03-14

我有以下示例类<code>Foo</code>和嵌套类<code<Bar</code<,所有内容都是<code>constexpr</code>:

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();
    }
};

我想测试调用<code>Foo::DoTheThing</code>返回1:

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

海湾合作委员会和Clang都在这里抱怨,但MSVC没有

GCC表示:

错误:constexpr Foo::Bar

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

还有Clang:

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

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

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

更有趣的是,如果我移除< code>constexpr,或者如果我将< code>Bar的定义移到< code>Foo之外,我可以让GCC和Clang正常工作。

请注意,这个问题的灵感来自以下方面:

    < Li > C 14中的简单constexpr LookUpTable(我的问题是这个未回答问题的一部分) < li >尽管嵌套结构与全局结构相同,但它会破坏constexpr(这似乎提供了一些关于正在发生的事情的见解)

共有1个答案

程俊健
2023-03-14

自n4140

§ 9.2.2 [类记忆](强调我的)

在类说明符的结束<code>}</code>处,类被视为完全定义的对象类型(3.9)(或完整类型)。在类成员规范中,该类在函数体、默认参数、使用引入继承构造函数的声明(12.9)、异常规范以及非静态数据成员的大括号或相等的初始化器(包括嵌套类中的此类内容)中被视为完整的。否则,它在其自身的类成员规范中被视为不完整的。

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

澄清一下,特别是考虑到这个问题:内部类的静态 constexpr 成员

我在上面引用的标准基本上意味着,除非另有说明,否则一个类被认为是不完整的。< code>static、< code>constexpr或< code>static constexpr初始值设定项不属于另外指定的部分,因此我们不能使用类中声明的任何内容,包括嵌套的类类型。

< sup > *意味着不能在类声明中使用它或它的成员。最广为人知的例外是在成员函数中。

 类似资料:
  • 考虑以下代码: GCC v6.1编译它,叮当声3.8拒绝它,错误如下: 2:错误:没有成员名为'foo'在'U' struct S{静态constexpr int bar=T::foo;}; 哪个编译器是对的? 会不会是因为在我们尝试在中使用它时不是一个完整的类型? 在这种情况下,它应该被认为是GCC的错误,但我想知道我是否正确之前在错误跟踪器上搜索/打开问题… 编辑 与此同时,我已经向GCC打开

  • 问题内容: 什么是静态嵌套类?静态和非静态嵌套类有什么区别? 问题答案: 静态内部类是嵌套在具有修饰符的另一个类中的类。除了可以访问在其内部定义的类的私有成员之外,它与顶级类几乎相同。 类是静态内部类。类是一个非静态的内部类。两者之间的区别是,非静态内部类的实例被永久连接到的实例-你不能创建一个没有。不过,您可以独立创建对象。 中的代码,并且都可以访问x; 不允许使用其他代码。

  • 实际代码更复杂,但我能够将其简化为这个示例。 在我尝试获取指向MyPackets\u t::type的指针(在main()中取消对foo()的注释调用)之前,一切都正常 此时,为了使应用程序链接,类型需要定义。 我正在努力寻找正确的定义语法。已注释掉模板。。。应该做到这一点。但是,它生成了一个错误“PacketCollection::types的模板参数与原始模板不匹配”。 尝试这样的东西-模板

  • 我有以下C 11代码(简化版): GCC 4.9.1可以很好地编译和链接此代码。另一方面,Clang 3.5.0抱怨未定义的引用: 哪个是对的?这个代码合法吗?我对静态Constexr成员规则的理解(主要基于这个问题)是,只有在获取变量的地址时才需要类外定义。但是我没有将Deriv的地址ed::信息或在任何地方使用对它的引用;我只是按值将它传递给Base构造函数。 我发现了各种变通方法: 使两个构

  • 在Oracle的Java教程中,我找到了以下文本: 与类方法和变量一样,静态嵌套类与其外部类关联。和静态类方法一样,静态嵌套类不能直接引用其封闭类中定义的实例变量或方法--它只能通过对象引用来使用它们。 注意:一个静态嵌套类与它的外部类(和其他类)的实例成员交互,就像任何其他顶级类一样。实际上,静态嵌套类在行为上是一个顶级类,为了方便打包,它已经嵌套在另一个顶级类中。 我认为不可能实例化一个静态类

  • 问题内容: 我很难用Java的非静态嵌套类来解决问题。考虑以下示例,该示例先显示“ Inner”,然后显示“ Child”。 我知道Inner实例始终必须与Outer实例相关联,这也适用于Child,因为它扩展了Inner。我的问题是语法的含义- 为什么调用内部构造函数? 我只看到一个用于调用超类构造函数和调用重写方法的超类版本的纯文本,但从未使用过这种形式。 问题答案: 这称为“合格的超类构造函