添加 'constexpr' 可以改变行为吗?

Can adding 'constexpr' change the behaviour?

给定两个程序,其中源代码中的唯一区别是存在或不存在一个 constexpr,程序的含义是否可能发生变化?

换句话说,如果有一个编译器选项要求编译器在可能的情况下非常努力地推断 constexpr,它是否会破坏现有的标准代码 and/or 以错误的方式改变其含义?

想象一下处理一个代码库,原始开发人员忘记在可能的地方包含 constexpr,也许是在 C++11 之前编写的代码。如果编译器可以推断 constexpr 来帮助您继续工作,那就太好了。当然,也许它还应该在每次进行此推断时发出警告,鼓励您稍后显式添加 constexpr。但它仍然会有用。我担心它可能会破坏东西?

到目前为止,我唯一能想到的是 constexpr 函数是隐式的 inline 并且在某些情况下添加 inline 会以不好的方式改变事情;例如,如果您打破单一定义规则。

Given two programs where the only difference in the source code is the presence or absence of one constexpr, is it possible that the meaning of the program changes?

是的,至少对于 constexpr 函数来说是这样。这就是不允许实现选择哪些标准函数被标记为 constexpr 的原因,主要问题是用户可能会通过 SFINAE 观察到不同的行为。这在 LWG issue 2013: Do library implementers have the freedom to add constexpr? 中有记录,它说 (emphasis mine):

Some concern expressed when presented to full committee for the vote to WP status that this issue had been resolved without sufficient thought of the consequences for diverging library implementations, as users may use SFINAE to observe different behavior from otherwise identical code. Issue moved back to Review status, and will be discussed again in Portland with a larger group. Note for Portland: John Spicer has agreed to represent Core's concerns during any such discussion within LWG.

有一个简单的技巧:

template<int n>struct i{};
int foo(int){return 0;}
constexpr int foo(char){return 'a';}

template<class T=int, T x=1,i<foo(x)>* =nullptr>
bool bar(){return true;}
template<class T=int, T x=1,class...Ts>
bool bar(Ts...){return false;}

如果 int foo(int) 是 constexpr,则默认选择 bar 的不同重载。

使用不同的代码 运行,任何行为都可能发生变化。

live example(只需更改注释掉的 #define X)。


示例设计:

char 重载可防止上述代码格式错误,无需诊断,因为所有模板都必须具有有效的特化。 foo<char> 供应。实际上,它的存在不是必需的:ADL 可以从远处找到一个 foo,在 some_type* 上过载,然后将 some_type* 作为 T 传递。这意味着没有编译单元可以证明代码格式错误。

Ts... 使得 bar 重载不太受欢迎。所以如果第一个匹配,就没有歧义。只有当第一个不匹配时(由于 foo(x) 不是 constexpr 导致的 SFINAE)才会调用第二个重载(或者,如果有人向它传递参数)。