【问题标题】:Criteria for a variable to be usable in a constant expression变量在常量表达式中可用的条件
【发布时间】:2019-08-07 07:10:52
【问题描述】:

这段代码在所有四大编译器上都能正常编译,即使在-pedantic上也是如此

struct S
{
  constexpr S(int a) {}
};

constexpr int f(S a)
{
  return 1;
}

int main()
{
  int a = 0;
  S s(a);
  constexpr int b = f(s);
}

但是,根据标准,这不应该是这样……对吧?首先,s 不能在常量表达式中使用[expr.const]/3,因为它不符合constexprconst 以及枚举或整数类型的标准。

其次,它不是 constant-initialized [expr.const]/2 因为初始化的完整表达式不会是 constant 表达式 [expr.const]/10 由于 lvalue-to在初始化构造函数的参数时,正在对一个在常量表达式中不可用的变量(a)执行-rvalue 转换。

所有这些编译器是否只是省略了构造函数参数的初始化,因为它没有副作用,并且它是否符合标准(我 99% 肯定它不是,这是它的唯一方法将是使sconstexpr,并将其传递给const int 或声明为constexprint)?

【问题讨论】:

  • 嗯,你的推理至少听起来是合理的......你可能忽略的一点是f根本不使用参数a,所以无论如何结果仍然是 constexpr 。 如果我就在这里,那么如果您将传递给S 的构造函数的参数存储在一个尚未添加的成员变量中并从f 中返回该成员变量,那么编译应该会失败。跨度>
  • 也许我读错了,但为什么会有a 的左值到右值转换? 如果需要,函数的参数(包括构造函数)只会被隐式转换(包括值转换)。将不合格的非引用 int 传递给接受不合格的非引用 int 作为参数的函数根本不需要任何转换或转换,只需该值的普通副本。
  • @Aconcagua 正确,如果值被存储,则编译失败,但是需要初始化参数(标准不允许省略初始化)
  • 不过,这种初始化没有任何副作用。这可能会产生一些影响。
  • expr3: s 未使用。 expr2:它是一个本地整数。编译器在编译时就知道该值。编译器很聪明。

标签: c++ language-lawyer


【解决方案1】:

我相信魔术师的诀窍是复制S。你省略了它,所以这里会为你生成一个默认的。现在它也是一个constexpr 函数。

[class.copy.ctor](强调我的)

12 一个默认的复制/移动构造函数,未定义为 在使用 odr ([basic.def.odr]) 时,deleted 是隐式定义的当它需要持续评估时 ([expr.const]),或者当它 在第一次声明后显式默认。 [ 注: 复制/移动构造函数是隐式定义的,即使实现 省略了它的 odr 用途([basic.def.odr]、[class.temporary])。 —— 尾注 ] 如果隐式定义的构造函数满足要求 constexpr 构造函数 ([dcl.constexpr]) 的隐式定义 构造函数是 constexpr

复制 c'tor 的评估是否与[expr.const]/4 中的任何点相冲突?它不是。它不对任何参数的成员执行左值到右值的转换(没有可以执行转换的)。它不会以任何方式使用其引用参数,这将要求所述引用在常量表达式中可用。所以我们确实得到了一个有效的常量表达式,尽管是一个不直观的表达式。

我们可以通过添加一个成员到S来验证上述内容。

struct S
{
  int a = 1;  
  constexpr S(int a) {}
};  

现在,复制 c'tor 正在尝试访问一个在常量表达式中不可用的对象,作为其评估的一部分(通过所述引用)。确实如此,compilers will complain

【讨论】:

  • @KrystianS 看起来[expr.const]/4 并没有说你不能提及常量表达式中的常量表达式中不可用的变量...
  • @KrystianS - 但它没有得到使用。复制 c'tor 负责产生一个值,并且它在不评估 s 的情况下这样做。
  • @KrystianS 参数是从参数中初始化的,这个初始化是constexpr。这使得表达式成为一个常量表达式。
  • @KrystianS - 什么在标准中要求,请告诉我?
  • @StoryTeller @L.F.现在想通了。不会发生左值到右值的转换(通常不会发生在类类型的对象中),因为s 绑定到(复制构造函数的)引用,并且复制构造函数调用本身是一个常量表达式。
猜你喜欢
  • 2010-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-17
  • 1970-01-01
  • 2014-07-04
  • 1970-01-01
  • 2015-08-31
相关资源
最近更新 更多