【问题标题】:Argument only used in unevaluated context within the body of a constexpr function仅在 constexpr 函数主体内的未评估上下文中使用的参数
【发布时间】:2016-01-21 13:47:08
【问题描述】:

以下代码已使用 gcc 5.3.0 成功编译,但无法使用 clang 3.7.0 编译。在这两种情况下,我都使用了具有相同命令行选项的在线 coliru 编译器:-std=c++14 -O2 -Wall -pedantic -pthread。

#include <cstdio>

// Definition of constexpr function 'foo'.
constexpr std::size_t foo(const int& arg_foo) { return sizeof(arg_foo); }

// Definition of function 'test'.
void test(const int& arg)
{ 
  // The following line produces an error with clang.
  constexpr std::size_t res_foo = foo(arg);

  // Print the result returned by the 'foo' function.
  std::printf("res_foo = %lu\n", res_foo);
}

// Definition of function 'main'.
int main(int argc, const char* argv[])
{
  // Test function call.
  test(argc);

  // Return statement.
  return 0;
}

clang 拒绝它并出现以下错误:

error: constexpr variable 'res_foo' must be initialized by a constant expression
constexpr size_t res_foo = foo(arg);
                           ~~~~^~~~

由于两个编译器之间的这种差异,我想知道这是否是一段有效的代码。如果没有,我想更好地理解为什么会这样。

【问题讨论】:

  • gcc 错误,是 constexpr 的众多错误之一
  • @TemplateRex 但是foo 以什么方式破坏了 constexpr 函数的任何规则?
  • @Archimaredes 不是,错误是test 中的arg 不是constexpr

标签: c++ c++14 constexpr


【解决方案1】:

您将constconstexpr 值混合在一起。 constexpr 的定义是编译时已知的值。但是 argcvariable 仅在运行时才知道(它是传递给可执行文件的多个参数)。因此,您不能将其分配给另一个 constexpr 变量 - res_foo。从 res_foo 定义中删除 constexpr 将使您的代码可编译。

constconstexpr 之间的区别可以简化为:
const - 我不会更改此值
constexpr - 此值在编译时已知时间我不会改变它

我的猜测是 GCC 能够使用 O2 编译此代码,因为您没有使用 arg_foo 参数并且它的大小在编译时是已知的。但它在语法上仍然不正确 - 编译器应该发出错误,因为非 constexpr 值被分配给 constexpr 变量。

【讨论】:

  • 我想他已经意识到了这一点,他的问题是因为他从来没有真正使用过argc的值,他只是取了sizeof,这是一个常量表达式。
  • @TartanLlama 为什么 gcc 能够使用 O2 编译此代码:它忽略了参数,因为它没有被使用,并在编译时返回一个众所周知的值。但它在语法上仍然不正确 - 编译器应该发出错误,因为非 constexpr 值被分配给了 constexpr 变量。
  • 我知道它在语法上仍然不正确,但我认为这就是你应该在答案中解决的问题。
  • @TartanLlama 你是对的。解决了您在答案中的评论。
【解决方案2】:

程序格式正确,因为foo(arg) 是 C++14 5.20/2 中定义的纯右值核心常量表达式。特别是在求值过程中没有左值到右值的转换,这将使它不是一个常量表达式。

【讨论】:

  • 并非如此。评估需要评估 id-expression arg,它与 [expr.const]/2.9 相冲突:“条件表达式 e 是核心常量表达式,除非 e 的评估,遵循抽象机 (1.9) 的规则,将评估以下表达式之一:[...] 一个 id 表达式,它引用引用类型的变量或数据成员,除非引用具有前面的初始化,并且 - 它已被初始化使用常量表达式或 — 它是对象的非静态数据成员,其生命周期从 e" 的求值开始。
  • 5/8 "未计算的操作数未计算。"
  • 是的,它没有在 foo 内部进行评估。但是它在它之外被评估以用于参数传递。评估foo(arg) 的第一步是评估fooarg
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-16
  • 1970-01-01
  • 2014-04-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多