【问题标题】:If structured bindings cannot be constexpr why can they be used in constexpr function?如果结构化绑定不能是 constexpr 为什么它们可以在 constexpr 函数中使用?
【发布时间】:2018-02-25 11:55:57
【问题描述】:

根据this answer 显然没有充分的理由为什么不允许结构化绑定是 constexpr,但标准仍然禁止它。但是,在这种情况下,不应该禁止在 constexpr 函数中使用结构化绑定吗?考虑一个简单的 sn-p:

#include <utility>

constexpr int foo(std::pair<int, int> p) {
    auto [a, b] = p;
    return a;
}

int main() {
    constexpr int a = foo({1, 2});
    static_assert(a == 1);
}

gccclang 都不会导致编译代码出现问题。代码格式是否错误,或者实际上是否允许这种代码?

【问题讨论】:

  • 这与可以在 constexpr 函数中创建和修改非常量变量的原因相同。
  • @cpplearner 好点!写一个答案让我接受。

标签: c++ language-lawyer c++17 constexpr structured-bindings


【解决方案1】:

在函数声明的情况下,constexpr 说明符是对编译器的断言,即声明的函数可以在 常量表达式 中求值,即可以在以下位置求值的表达式编译时。然而,声明中的对象初始化不需要在其声明说明符中包含 constexpr 以成为 常量表达式

短一点:constexpr 函数可能暗示常量表达式,但常量表达式初始化不需要相关声明具有constexpr 说明符。

您可以在 C++ 标准 [dcl.constexpr] 中检查这一点:

调用 constexpr 函数产生的结果与调用等效的非 constexpr 函数的结果相同 除此以外的所有方面

——对 constexpr 函数的调用可以出现在常量表达式中[...]

这是一个表达式的求值,用于确定表达式是否为 常量表达式[expr.const]:

表达式 e 是核心常量表达式,除非 e [...] 的 求值 [...] 将求值以下 表达式[...]

声明不是表达式,因此被声明的对象的初始化是一个常量表达式,无论在宣言。

最后,在 [dcl.constexpr] 中,指定 constexpr 函数必须存在可以将其主体作为常量表达式评估的参数

对于既不是默认也不是模板的 constexpr 函数或 constexpr 构造函数,如果没有参数 值的存在使得函数或构造函数的调用可以是评估的子表达式 核心常量表达式(8.20),或者,对于构造函数,某个对象的常量初始化器(6.6.2), 程序格式错误,无需诊断。

当您声明constexpr int a 时,编译器期望a 被一个常量表达式初始化,而表达式foo({1,2}) 是一个常量表达式,因此您的代码格式正确。

PS:尽管如此,函数局部变量声明中的声明说明符(static,thread_local=>static)暗示该函数不能被声明为constexpr

【讨论】:

  • 谢谢!我讨厌不合理的情况,也许这就是为什么我被困在案例代码格式不正确的想法中,但是如果将其设置为不正确格式,则不正确的后果会更大。
  • 哦,我不是说你的回答无关紧要,只是这件事的标准。想一想——当你不想让某人从门进来的时候,为什么要从窗户里邀请他?
  • @W.F.哦,这只是当我在发布它几个小时后阅读我的答案时,我想知道这怎么可能是你问题的答案!实际上,我花了一段时间才感觉到空白,所以我想我需要发布它。当我读到你的评论时,我想重用你的词汇,但我的英语,在技术背景下,很差。我当然不理解你,我可能没有在之前的评论中表达我想要的。我不想冒犯你。
【解决方案2】:

several requirements that a constexpr function must meetconstexpr 函数的主体有一些要求,显示的代码似乎没有违反任何要求。关键是不要求函数中的每条语句都必须是constexpr。这里唯一有趣的要求是:

至少存在一组参数值,使得 函数的调用可以是 a 的求值子表达式 核心常量表达式(对于构造函数,在常量中使用 初始化程序就足够了)(C++14 起)。无需诊断 违反此项目符号。

注意最后一句话。编译器可能会(但不是必须)抛出一个危险信号。

关键要求仅仅是函数有各种参数值,从而导致函数的结果保持不变(并且函数体满足列出的要求)。例如,函数可能有条件地使用结构化绑定;但是对于某些参数值集,请执行其他操作,从而产生恒定的结果。这将为 constexpr 函数勾选此复选框。

但是,尽管现代 C++ 编译器非常复杂,但它们可能不一定能够在所有可能的情况下都达到这一决定,因此,在实践中,很难强制执行这样的要求,因此允许编译器只认为这是理所当然的。

【讨论】:

  • 所以你的意思是虽然代码格式不正确,但编译器可以接受它?
  • 由于foo({1, 2}) is 函数调用可能是(并且现在是)核心常量表达式的求值子表达式,我不认为这个项目符号被违反因此是否需要对违反此项目符号的诊断进行诊断。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-15
  • 2016-11-19
  • 2020-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多