在 constexpr 中使用 argc,是否严格要求所涉及的任何子表达式都是常量表达式?
Using argc in a constexpr, is it strictly required that any sub-expression involved be a constant expression?
示例:
int main(int argc, char**)
{
constexpr int a = argc * 0;
(void)a;
constexpr int b = argc - argc;
(void)b;
return 0;
}
argc
不是常量表达式,但编译器仍然能够在编译时计算 a
和 b
的结果(即 0
)两种情况。
g++ accepts the code above, while clang 和 MSVC14 拒绝它。
标准是否允许编译器在 constexpr
方面像 g++ 一样智能?
你可以看这里:
constexpr at cppreference.com
基线是 constexpr 应该是可以在编译时明确确定的东西,以便编译器可以在计算表达式时替换它的值。
我认为问题在于 argc 不是 constexpr。编译器不知道。事实上,您可以使用不同数量的参数执行 main。如果 gcc 接受它,它可能是一个错误,或者可能涉及一些我不明白的细微之处。
argc * 0
和 argc - argc
都不是常量表达式,在某些情况下允许左值到右值的转换 none 它们适用于此处。如果我们看一下 C++11 标准草案 5.19
[expr.const] 它列出了例外情况。它说:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially
evaluated subexpression [...]
并且有几个项目符号,包括以下关于左值到右值的转换:
an lvalue-to-rvalue conversion (4.1) unless it is applied to
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding
initialization, initialized with a constant expression, or
a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers
to a sub-object of such an object, or
a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not
ended, initialized with a constant expression;
有趣的是gcc不接受下面的代码(see it live):
constexpr int a = argc * 2;
所以看起来 gcc 是说我知道结果将为零,因此它执行常量折叠并且不需要执行 argc
的左值到右值转换来确定结果。
遗憾的是,我在 5.19
部分中没有看到任何允许这种短路的规定。这看起来与 which has a bug report but no one from the gcc team has replied to that one. I added a comment 中的情况非常相似,该错误报告表明这似乎相关。
马克在下面的评论表明这是一个错误:
There is a whole c++-delayed-folding branch on which some gcc developers are working, which will delay a number of optimizations and might fix this. It is important for other reasons, rejecting the code in this question is very low priority
常量表达式是否严格要求每个子表达式都是常量表达式?不,例如 5.19
它说: (emphasis mine)
A conditional-expression is a core constant expression unless it involves one of the following as a potentially
evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional
(5.16) operations that are not evaluated are not considered [...]
所以下面是一个常量表达式:
constexpr int a = false && argc * 0;
因为 argc * 0
没有计算,因为 &&
计算从左到右和短路。
示例:
int main(int argc, char**)
{
constexpr int a = argc * 0;
(void)a;
constexpr int b = argc - argc;
(void)b;
return 0;
}
argc
不是常量表达式,但编译器仍然能够在编译时计算 a
和 b
的结果(即 0
)两种情况。
g++ accepts the code above, while clang 和 MSVC14 拒绝它。
标准是否允许编译器在 constexpr
方面像 g++ 一样智能?
你可以看这里: constexpr at cppreference.com
基线是 constexpr 应该是可以在编译时明确确定的东西,以便编译器可以在计算表达式时替换它的值。
我认为问题在于 argc 不是 constexpr。编译器不知道。事实上,您可以使用不同数量的参数执行 main。如果 gcc 接受它,它可能是一个错误,或者可能涉及一些我不明白的细微之处。
argc * 0
和 argc - argc
都不是常量表达式,在某些情况下允许左值到右值的转换 none 它们适用于此处。如果我们看一下 C++11 标准草案 5.19
[expr.const] 它列出了例外情况。它说:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [...]
并且有几个项目符号,包括以下关于左值到右值的转换:
an lvalue-to-rvalue conversion (4.1) unless it is applied to
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression, or
a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an object, or
a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not ended, initialized with a constant expression;
有趣的是gcc不接受下面的代码(see it live):
constexpr int a = argc * 2;
所以看起来 gcc 是说我知道结果将为零,因此它执行常量折叠并且不需要执行 argc
的左值到右值转换来确定结果。
遗憾的是,我在 5.19
部分中没有看到任何允许这种短路的规定。这看起来与
马克在下面的评论表明这是一个错误:
There is a whole c++-delayed-folding branch on which some gcc developers are working, which will delay a number of optimizations and might fix this. It is important for other reasons, rejecting the code in this question is very low priority
常量表达式是否严格要求每个子表达式都是常量表达式?不,例如 5.19
它说: (emphasis mine)
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [...]
所以下面是一个常量表达式:
constexpr int a = false && argc * 0;
因为 argc * 0
没有计算,因为 &&
计算从左到右和短路。