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

Using argc in a constexpr, is it strictly required that any sub-expression involved be a constant expression?

本文关键字:表达式 任何 常量 constexpr argc 严格要求 是否      更新时间:2023-10-16

示例:

int main(int argc, char**)
{
    constexpr int a = argc * 0;
    (void)a;
    constexpr int b = argc - argc;
    (void)b;
    return 0;
}

argc不是一个常量表达式,但编译器仍然能够在编译时(即0(计算ab的结果。

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

标准是否允许编译器在constexpr方面像g++一样智能?

argc * 0argc - argc都不是常量表达式,在某些情况下允许左值到右值的转换,它们都不适用于此处。如果我们看一下C++11标准部分5.19[expr.const]的草案,它列出了例外情况。上面写着:

条件表达式是核心常量表达式,除非它包含以下其中一个作为潜在的已评估子表达式[…]

并且有几个项目符号,包括以下关于左值到右值转换的项目符号:

左值到右值的转换(4.1(,除非它应用于

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

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

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

值得注意的是,gcc不接受以下代码(请参阅直播(:

constexpr int a = argc * 2;

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

不幸的是,我在5.19节中没有看到任何允许这种短路的规定。这看起来和int a=1的情况非常相似,a||1是常量表达式吗?它有一个错误报告,但gcc团队没有人回复。我在错误报告中添加了一条注释,表明这似乎是相关的。

Mark在下面的评论表明这是一个错误:

一些gcc开发人员正在研究一个完整的c++延迟折叠分支,这将延迟一些优化,并可能解决这个问题。由于其他原因,拒绝这个问题中的代码是非常低优先级的,这一点很重要

常量表达式是否严格要求每个子表达式都是常量表达式?不,例如5.19中说:(emphasis mine(

条件表达式是核心常量表达式,除非它包含以下其中一个作为潜在的求值的子表达式(3.2(,,但逻辑AND(5.14(、逻辑OR(5.15(和条件的子表达式(5.16(未经评估的操作不被视为[…]

因此以下是一个常量表达式:

constexpr int a = false && argc * 0;

因为CCD_ 13从左到右评估短路,所以不评估CCD_。

您可以在这里查看:cppreference.com 上的constexpr

基线是constexpr应该是在编译时可以明确确定的东西,这样编译器就可以在计算表达式时替换它的值。

我认为问题在于argc不是constexpr。编译器不知道。事实上,您可以使用不同数量的参数来执行main。如果gcc接受它,它可能是一个bug,或者可能有一些我不明白的微妙之处。

相关文章: