繁体   English   中英

constexpr 使用静态函数初始化静态成员

[英]constexpr initializing static member using static function

要求

我想要从constexpr函数计算的constexpr值(即编译时常量)。 而且我希望这两个范围都在类的命名空间内,即类的静态方法和静态成员。

第一次尝试

我首先用(对我来说)显而易见的方式写了这个:

class C1 {
  constexpr static int foo(int x) { return x + 1; }
  constexpr static int bar = foo(sizeof(int));
};

g++-4.5.3 -std=gnu++0x说:

error: ‘static int C1::foo(int)’ cannot appear in a constant-expression
error: a function call cannot appear in a constant-expression

g++-4.6.3 -std=gnu++0x抱怨:

error: field initializer is not constant

第二次尝试

好吧,我想,也许我必须把东西移出类体。 所以我尝试了以下方法:

class C2 {
  constexpr static int foo(int x) { return x + 1; }
  constexpr static int bar;
};
constexpr int C2::bar = C2::foo(sizeof(int));

g++-4.5.3将毫无怨言地编译它。 不幸的是,我的其他代码使用了一些基于范围的for循环,所以我必须至少有 4.6。 现在我仔细查看了支持列表,似乎constexpr也需要 4.6。 使用g++-4.6.3我得到

3:24: error: constexpr static data member ‘bar’ must have an initializer
5:19: error: redeclaration ‘C2::bar’ differs in ‘constexpr’
3:24: error: from previous declaration ‘C2::bar’
5:19: error: ‘C2::bar’ declared ‘constexpr’ outside its class
5:19: error: declaration of ‘const int C2::bar’ outside of class is not definition [-fpermissive]

这对我来说听起来很奇怪。 这里的东西“在constexpr有何不同”? 我不想添加-fpermissive因为我更喜欢严格检查我的其他代码。 foo实现移到类体之外没有明显的效果。

预期答案

有人可以解释这里发生了什么吗? 我怎样才能实现我正在尝试做的事情? 我主要对以下类型的答案感兴趣:

  • 一种在 gcc-4.6 中完成这项工作的方法
  • 观察到后来的 gcc 版本可以正确处理其中一个版本
  • 一个指向规范的指针,根据该规范,我的至少一个构造应该可以工作,以便我可以让 gcc 开发人员真正让它工作
  • 根据规范,我想要的信息是不可能的,最好是对此限制背后的基本原理有所了解

其他有用的答案也是受欢迎的,但可能不会那么容易被接受。

该标准要求(第 9.4.2 节):

可以在类定义中使用constexpr说明符声明文字类型的static数据成员; 如果是这样,它的声明应指定一个大括号或等号初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式。

在您的“第二次尝试”和 Ilya 的答案中的代码中,声明没有大括号或相等的初始化程序

你的第一个代码是正确的。 不幸的是 gcc 4.6 不接受它,而且我不知道在哪里可以方便地尝试 4.7.x(例如 ideone.com 仍然停留在 gcc 4.5 上)。

这是不可能的,因为不幸的是,标准禁止在类完成的任何上下文中初始化静态constexpr数据成员。 9.2p2 中大括号或相等初始化器的特殊规则仅适用于非静态数据成员,但这是静态的。

最可能的原因是constexpr变量必须作为编译时常量表达式从成员函数体内部可用,因此变量初始值设定项在函数体之前完全定义——这意味着函数仍然不完整(未定义) ) 在初始值设定项的上下文中,然后此规则生效,使表达式不是常量表达式:

未定义的调用constexpr功能或未定义constexpr a的定义之外构造constexpr函数或constexpr构造;

考虑:

class C1
{
  constexpr static int foo(int x) { return x + bar; }
  constexpr static int bar = foo(sizeof(int));
};

1) Ilya 的示例应该是无效代码,因为静态 constexpr 数据成员 bar被初始化为违反标准中的以下语句:

9.4.2 [class.static.data] p3: ... 可以在类定义中使用 constexpr 说明符声明文字类型的静态数据成员; 如果是这样,它的声明应指定一个大括号或等号初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式。

2)MvG问题中的代码:

class C1 {
  constexpr static int foo(int x) { return x + 1; }
  constexpr static int bar = foo(sizeof(int));
};

就我所见和直觉上来说是有效的,因为静态成员 foo(int)是由bar开始的时间处理定义的(假设自上而下处理)。 一些事实:

  • 我确实同意,尽管在调用 foo 时类 C1并不完整(基于 9.2p2),但是类 C1 的完整性或不完整性并没有说明就标准而言是否定义了 foo。
  • 我确实搜索了成员函数定义的标准,但没有找到任何东西。
  • 因此,如果我的逻辑有效,Ben 提到的陈述不适用于这里:

    在 constexpr 函数或 constexpr 构造函数的定义之外调用未定义的 constexpr 函数或未定义的 constexpr 构造函数;

3)Ben给出的最后一个例子,简化了:

 class C1 { constexpr static int foo() { return bar; } constexpr static int bar = foo(); };

看起来无效,但出于不同的原因,而不仅仅是因为foobar的初始值设定项中被调用。 逻辑如下:

  • foo()静态 constexpr 成员 bar的初始化程序中被调用,因此它必须是一个常量表达式(9.4.2 p3)。
  • 因为它是对 constexpr 函数的调用,所以函数调用替换(7.1.5 p5) 开始了。
  • 它们没有函数的参数,所以剩下的是“将结果返回的表达式或花括号初始化列表隐式转换为函数的返回类型,就像通过复制初始化一样。” (7.1.5 p5)
  • 返回表达式只是bar ,它是一个左值,需要进行左值到右值的转换。
  • 但通过在子弹9(5.19 P2),该不满足,因为它尚未初始化:

    • 左值到右值的转换 (4.1) 除非它应用于:
      • 整数或枚举类型的泛左值,它引用具有前面初始化的非易失性 const 对象,用常量表达式初始化。
  • 因此bar的左值到右值转换不会产生不符合 (9.4.2 p3) 要求的常量表达式。

  • 因此,根据 (5.19 p2) 中的第 4 项,对foo()的调用不是常量表达式:

    调用带有参数的 constexpr 函数,当被函数调用替换 (7.1.5) 替换时,不会产生常量表达式

#include <iostream>

class C1 
{
public:
    constexpr static int foo(constexpr int x)
    { 
        return x + 1;
    }

    static constexpr int bar;
};

constexpr int C1::bar = C1::foo(sizeof(int));

int main()
{
    std::cout << C1::bar << std::endl;
    return 0;
}

这种初始化效果很好,但只适用于 clang

可能,这里的问题与类中声明/定义的顺序有关。 众所周知,您甚至可以在类中声明/定义任何成员之前使用它。

当您在类中定义 de constexpr 值时,编译器没有可用的 constexpr 函数,因为它在类内部。

也许,与此想法相关的Philip回答是理解问题的好点。

请注意此代码编译没有问题:

constexpr int fooext(int x) { return x + 1; }
struct C1 {
  constexpr static int foo(int x) { return x + 1; }
  constexpr static int bar = fooext(5);
};

constexpr static int barext = C1::foo(5);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM