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

通过使用派生类的静态Constexpr数据成员初始化基类的静态Constexpr数据成员

景英杰
2023-03-14

考虑以下代码:

template<typename T>
struct S { static constexpr int bar = T::foo; };

struct U: S<U> { static constexpr int foo = 42; };

int main() { }

GCC v6.1编译它,叮当声3.8拒绝它,错误如下:

2:错误:没有成员名为'foo'在'U'
struct S{静态constexpr int bar=T::foo;};

哪个编译器是对的?
会不会是因为U在我们尝试在S中使用它时不是一个完整的类型?
在这种情况下,它应该被认为是GCC的错误,但我想知道我是否正确之前在错误跟踪器上搜索/打开问题…

编辑

与此同时,我已经向GCC打开了一个bug。< br >等待它接受答案。

共有1个答案

沈栋
2023-03-14

对于C 14和11,克朗是对的;但是,在最新的工作草案(未来的C 17)中情况发生了变化 - 请参阅下一节。

要寻找的标准报价是(来自N4140,最接近C 14的草稿):

[temp.inst]/1:

[...类模板专门化的隐式实例化导致声明的隐式实例化,但不导致类成员函数、成员类、限定范围的成员枚举、静态数据成员和成员模板的定义、默认参数或异常规范的隐式实例化。[...]

[温度点]/4:

对于类模板特化,[…]这种特化的实例化点紧接在引用特化的命名空间范围声明或定义之前。

因此,< code >的实例化点

[class.static.data]/3:

[...]可以使用 constexpr 说明符在类定义中声明文本类型的静态数据成员;如果是这样,则其声明应指定一个大括号或相等的初始值设定项,其中作为赋值表达式的每个初始值设定项子句都是常量表达式。[...]如果成员在程序中被 odr 使用 (3.2),并且命名空间范围定义不应包含初始值设定项,则该成员仍应在命名空间作用域中定义。

根据上面引用的段落,S 定义中的 bar 的声明,即使它有一个初始值设定项,仍然只是一个声明,而不是一个定义,所以它在 S 时被实例化

解决方法是使bar成为一个函数;根据第一个引号,在隐式实例化s时不会实例化函数的定义

template<class T> struct S 
{
   static constexpr int bar() { return T::foo; }
};

struct U : S<U> { static constexpr int foo = 42; };

int main()
{
   constexpr int b = U::bar();
   static_assert(b == 42, "oops");
}

在工作草案(现为N4606)通过P0386R2之后,对[静态类别]/3进行了修订;相关部分现在显示为:

[...]内联静态数据成员可以在类定义中定义,并且可以指定大括号或等于初始值设定项。如果使用 constexpr 说明符声明该成员,则可以在没有初始值设定项的情况下在命名空间范围内重新声明该成员(此用法已弃用,请参阅 D.1)。[...]

对[basic.def]/2.3的更改对此进行了补充:

声明就是定义,除非:< br> [...]

    < li >它在类定义中声明了一个非内联静态数据成员(9.2,9.2.3),

[...]

因此,如果它是内联的,那么它是一个定义(有或没有初始化器)。[dcl.constexpr]/1说:

[…]使用stanexpr说明符声明的函数或静态数据成员隐含地是内联函数或变量(7.1.6)。[…]

这意味着 bar 的声明现在是一个定义,根据上一节中的引号,它没有为 S 的隐式实例化进行实例化。

当前工作草案中的[depr.static_constexpr]中的示例很好地总结了这种情况下的变化:

struct A {
   static constexpr int n = 5; // definition (declaration in C++ 2014)
};

const int A::n; // redundant declaration (definition in C++ 2014)

这使得GCC的行为在C 1z模式下符合标准。

 类似资料:
  • 我有以下示例类<code>Foo</code>和嵌套类<code<Bar</code<,所有内容都是<code>constexpr</code>: 我想测试调用<code>Foo::DoTheThing</code>返回1: 海湾合作委员会和Clang都在这里抱怨,但MSVC没有 GCC表示: 错误: ,或者如果我将< code>Bar的定义移到< code>Foo之外,我可以让GCC和Clang

  • 这是有效代码: 但是在这里,我真的很想声明和 。但是为什么呢?

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

  • 此代码编译: 该代码不: 报告的错误(在 MSVC、gcc 和 clang 中)表明他们认为 构造函数未定义或不是 ,例如。从叮叮当当: 为什么? (可能与这个问题有关,但在使用时应该是完整的;只有仍然不完整。

  • 问题内容: 当我们序列化对象时,静态成员不会被序列化,但是如果需要的话,还有什么办法吗? 问题答案: 第一个问题是为什么需要序列化静态成员? 静态成员与类相关联,而不与实例相关联,因此在序列化实例时将它们包括在内是没有意义的。 第一个解决方案是使那些成员不是静态的。或者,如果这些成员在原始类和目标类中是相同的(相同的类,但可能是不同的运行时环境),则根本不要序列化它们。 我对如何在静态成员之间进行

  • Singleton设计模式说我们应该在“single instance”类中定义一个私有静态属性。但是,没有适当的解释为什么数据成员必须是私有静态的。如果数据成员只是私有的,会有什么不同吗? 在以下代码中: 如果数据成员不是静态的,会有什么不同吗? 编辑:通过公开析构函数,它会改变单例设计的属性吗?