C++ constexpr - 可以在编译时计算值吗?

8
我正在阅读关于constexpr的文章,可以在这里看到更多相关内容:

constexpr指定符声明函数或变量的值可以在编译时计算。

起初我完全理解这句话的意思。然而,最近我遇到了一些完全让我不知所措的代码。下面是一个简单的示例:
#include <iostream>

void MysteryFunction(int *p);

constexpr int PlusOne(int input) {
  return input + 1;
}

int main() {
  int i = 0;
  MysteryFunction(&i);
  std::cout << PlusOne(i) << std::endl;

  return 0;
}

看这段代码,我无法确定PlusOne(i)的结果应该是什么,但它确实可以编译通过!(当然链接会失败,但g++ -std=c++11 -c却没有错误。)

“可以在编译时计算函数值”的正确解释是什么?


5
如果你将一个非 constexpr 的参数传递给 PlusOne 函数,编译器将无法在编译时评估它,它将仅成为一个普通的运行时函数。 - Some programmer dude
6
constexprе…ій”®еӯ—иЎЁзӨәеҮҪж•°жҲ–еҸҳйҮҸзҡ„еҖјеҸҜд»ҘеңЁзј–иҜ‘ж—¶иў«и®Ўз®—еҮәжқҘпјҲдҪҶдёҚдёҖе®ҡдјҡиў«еңЁзј–иҜ‘ж—¶и®Ўз®—пјүгҖӮ - Rakete1111
1
就此而言,cppreference不是C++的“官方描述”。它是一个社区管理的维基百科,尽管是高质量的维基百科。cppreference中的语言故意比标准本身更加随意。请勿将cppreference的陈述视为约束性的;如果您想成为一名语言律师,请参考标准本身(我喜欢这个网站:http://eel.is/c++draft/)。 - Justin
1
我不是在试图成为语言律师,我只是想理解功能。 - Apollys supports Monica
1
很遗憾,在现今的C++中,这两者经常是不可分割的 :P - Lightness Races in Orbit
显示剩余3条评论
4个回答

12

constexpr函数可以在常量表达式内被调用,前提是满足常量表达式的其他要求。它也可以在非常量表达式内被调用,在这种情况下,它的行为与未使用constexpr声明相同。正如您问题中的代码所示,调用constexpr函数的结果不会自动成为常量表达式。


这更有意义,这就是我猜测的,但似乎这不是我引用的句子的字面解释。 - Apollys supports Monica

12
引用的措辞有点误导。 如果你只考虑PlusOne本身,并观察它的逻辑,并假设输入在编译时已知,则其中的计算也可以在编译时执行。 在其上加上constexpr关键字可确保我们维护此可爱的状态并且一切正常。
但是如果输入在编译时已知,则它仍然只是一个普通的函数,并将在运行时调用。
因此,constexpr是函数的属性(“可以在编译时评估”对于某些输入,而不是对于所有输入),而不是您的函数/输入组合在此特定情况下的属性(因此也不是对于此特定输入)。
这有点像函数可以采用const int&,但这并不意味着原始对象必须是const。 在这里,类似地,constexpr在添加约束到函数的同时,并没有添加约束到函数的输入。
诚然,这都是一个巨大、混乱、模糊的问题(C++!耶!)。只需记住,您的代码描述程序的含义!它不是在编译的不同阶段的机器指令的直接配方。
(要真正执行此操作,您需要将整数作为模板参数。)

1
这个答案显然是正确的。我只想补充一点,如果你在一个变量上添加constexpr,它会在编译时强制执行。所以<code> constexpr auto j = PlusOne(i); // main.cpp:13:31: error: the value of 'i' is not usable in a constant expression constexpr auto j = PlusOne(5); // OK </code> - Jay Miller

8
如果函数的所有参数都可以在编译时计算出来,那么函数的返回值可以在编译时计算。但是,如果函数的参数值只能在运行时确定,那么函数的返回值只能在运行时计算。因此,“可以在编译时计算函数的值”是可能的,但不是必需的。

谢谢,我认为官方描述措辞不够完整/清晰明了。 - Apollys supports Monica
1
@Apollys,这还不是最糟糕的 :) 看看实际标准,你就会知道我在说什么。这就是为什么我们需要语言律师的原因。 - R Sahu

4

所有答案都是正确的,但我想给出一个简短的例子来解释constexpr有多么不直观。

#include <cstdlib>
constexpr int fun(int i){
    if (i==42){
        return 47;
    } else {
        return rand();
    }
}

int main()
{ 
    int arr[fun(42)];
}

作为一则附注: 一些人认为constexpr的状态不令人满意,因此他们提出了constexpr!关键字添加到语言中。

2
作为补充说明,以防有人后来来到这里 - constexpr!已被接受,但使用替代名称consteval。 :) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1073r3.html https://en.cppreference.com/w/cpp/language/consteval - Alex Meiburg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接