C++ 为什么constexpr适用于不纯函数

C++ 为什么constexpr适用于不纯函数,c++,c++11,constexpr,C++,C++11,Constexpr,在浏览我在constexpr上的一个老问题时,我无意中发现了一条非常重要的评论。 基本上可以归结为: (这是合法的C++11:() 我的问题是,标准允许这样做的原因是什么。因为我是引用透明的忠实粉丝,我希望他们有一个好的理由:)我想知道 顺便说一句,有相关的Q,但大多数A甚至并没有提到纯粹的东西,或者当他们提到的时候,他们并没有具体说明为什么std允许这样做。 函数定义中的关键字constepr告诉编译器,如果所有参数和变量在编译时都已知,则此函数可以在编译时执行。但是,没有这样的保证,例如,某

在浏览我在constexpr上的一个老问题时,我无意中发现了一条非常重要的评论。 基本上可以归结为: (这是合法的C++11:()

我的问题是,标准允许这样做的原因是什么。因为我是引用透明的忠实粉丝,我希望他们有一个好的理由:)我想知道

顺便说一句,有相关的Q,但大多数A甚至并没有提到纯粹的东西,或者当他们提到的时候,他们并没有具体说明为什么std允许这样做。

函数定义中的关键字
constepr
告诉编译器,如果所有参数和变量在编译时都已知,则此函数可以在编译时执行。但是,没有这样的保证,例如,某些值只能在运行时知道,在这种情况下,函数将在运行时执行

但是,它与pure或pupure无关,因为这些术语意味着输出仅依赖于输入,并且无论您使用相同的输入参数值调用函数多少次,无论是在编译时还是在运行时计算,每次输出都是相同的

例如

constexpr int add(int a, int b) { return a + b; } //pure!

const int a = 2, b = 3; //const
int c = 2, d = 3;       //non-const

//we may read update c and d here!

const int v1 = add(2,3);  //computed at compile-time
const int v2 = add(a,3);  //computed at compile-time
const int v3 = add(2,b);  //computed at compile-time
const int v4 = add(a,b);  //computed at compile-time

const int v3 = add(c,3);  //computed at runtime
const int v3 = add(c,b);  //computed at runtime
const int v3 = add(a,d);  //computed at runtime
const int v3 = add(c,d);  //computed at runtime

请注意,这里的
add
是一个纯函数,无论它是在编译时还是在运行时计算。

因为对于某些输入参数域,将永远不会采用不纯路径。对于该域,constexpr可以正常工作

例如,您的函数可能有一个简单的分支和一个更复杂的分支。您可以指定,要使函数在常量表达式中可用,函数参数必须满足这个和那个条件,从而使函数中的简单分支始终是纯的


这样做的一个有用的副作用是,在常量计算过程中可能会导致错误。也就是说,如果违反了简单分支中的先决条件,您可能会导致对不纯表达式的求值,从而引发编译时错误(在这里,断言或异常是个好主意,因为在运行时上下文中调用函数时,它会继续抱怨)。

在标准中,相关要求隐藏在
constexpr
功能的主要要求列表下方。第7.1.5/5节:

对于constexpr函数,如果不存在函数参数值,使得函数调用替换将产生一个常量表达式(5.19),则程序格式错误;无需诊断

§5.19定义了常量表达式的要求,因此您不能调用
rand()

宽松的限制允许您拥有条件纯的函数。您的示例
f(true)
是有效的模板参数,但
f(false)
不是

当然,缺点是编译器不会验证
constexpr
函数是否可以实际用于预期目的。您需要编写测试用例


啊,利特的回答也是正确的。(但这句话的措辞更简单。)

不,不是,这就是我为什么要说;)在commentsAny中,任何纯函数都可以在编译时根据“如同”规则进行求值,而不考虑
constexpr
。这叫做恒定折叠。您的示例中的
constepr
似乎没有任何区别。@Potatoswatter:您的意思是我们可以编写
std::array a即使
add
不是
constexpr
?我不这么认为。我在你的例子中指的是:v)。(你的答案似乎根本没有提到常量表达式上下文。)我想知道
std::array
是否会是一件坏事。@Potatoswatter:实际上我保持了示例的简单性,否则
constexpr
会对函数体施加很多限制,我不想在这个答案中讨论这些限制,因为这似乎不是这个问题的重点(?)。好吧,只要提到
std::array
与您之前提到的本质上是不同的。但是,示例中的
constexpr
没有任何作用,因为其中没有常量表达式上下文。它可以归结为带或不带
constexpr
的恒定传播。这也是一个更好的答案,因为它引用了标准的相关部分,并对其含义进行了很好的解释。
constexpr int add(int a, int b) { return a + b; } //pure!

const int a = 2, b = 3; //const
int c = 2, d = 3;       //non-const

//we may read update c and d here!

const int v1 = add(2,3);  //computed at compile-time
const int v2 = add(a,3);  //computed at compile-time
const int v3 = add(2,b);  //computed at compile-time
const int v4 = add(a,b);  //computed at compile-time

const int v3 = add(c,3);  //computed at runtime
const int v3 = add(c,b);  //computed at runtime
const int v3 = add(a,d);  //computed at runtime
const int v3 = add(c,d);  //computed at runtime