在constexpr中使用argc,是否严格要求所涉及的任何子表达式都是常量表达式?

Jam*_*ree 7 c++ gcc language-lawyer constexpr c++11

例:

int main(int argc, char**)
{
    constexpr int a = argc * 0;
    (void)a;
    constexpr int b = argc - argc;
    (void)b;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

argc不是常量表达式,但编译器仍能够在两种情况下计算结果ab编译时间(即0).

g ++接受上面的代码,而clang和MSVC14拒绝它.

标准是否允许编译器与g ++一样聪明constexpr

Sha*_*our 7

既不是argc * 0也不argc - argc是常量表达式,在某些情况下允许左值到右值的转换,这些都不适用于此.如果我们查看草案C++ 11标准部分5.19[expr.const],它会列出异常.它说:

条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式[...]

并且有几个子弹,包括以下关于左值到右值的转换:

除非适用,否则左值到右值的转换(4.1)

  • 一个整数或枚举类型的glvalue,它引用一个带有前面初始化的非易失性const对象,用一个常量表达式初始化,或者

  • 一个文字类型的glvalue,它指的是用constexpr定义的非易失性对象,或者指的是这样一个对象的子对象,或者

  • 一个文字类型的glvalue,它引用一个生命周期尚未结束的非易失性临时对象,用一个常量表达式初始化;

有趣的是,gcc不接受以下代码(现场直播):

constexpr int a = argc * 2;
Run Code Online (Sandbox Code Playgroud)

所以看起来gcc说我知道结果将为零,因此它执行常量折叠,并且不需要执行左值到右值的转换argc来确定结果.

不幸的是,我没有在章节5.19中看到任何允许这种短路的规定.这看起来非常类似于int a = 1中的情况,是一个|| 1一个常数表达式?有一个错误报告,但gcc团队中没有人回复那个.我在该错误报告中添加了评论,表明这似乎与此相关.

马克的评论如下表明这是一个错误:

有一个完整的c ++ - 延迟折叠分支,一些gcc开发人员正在使用它,这将延迟一些优化并可能解决这个问题.其他原因很重要,拒绝此问题中的代码的优先级非常低

常量表达式是否严格要求每个子表达式都是常量表达式?不,例如5.19它说:( 强调我的)

条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式(3.2),但是未评估的逻辑AND(5.14),逻辑OR(5.15)和条件(5.16)操作的子表达式不被视为 [...]

所以以下是一个常量表达式:

constexpr int a = false && argc * 0;
Run Code Online (Sandbox Code Playgroud)

因为argc * 0评估&&从左到右和短路,所以不进行评估.