Pot*_*ter 45 c++ constexpr c++11
C++ 11允许使用说明constexpr符声明的函数用于常量表达式,例如模板参数.对于允许的内容有严格的要求constexpr; 本质上这样的函数只包含一个子表达式,而不包含任何其他子表达式.(编辑:这在C++ 14中是放松的,但问题是.)
为什么要求关键字?得到了什么?
它确实有助于揭示接口的意图,但它不会通过保证函数在常量表达式中可用来验证该意图.编写constexpr函数后,程序员必须仍然:
与揭示意图相反,装饰功能constexpr可能会增加错误的安全感,因为在忽略中心语义约束的同时检查了切向句法约束.
简而言之:如果constexpr函数声明仅仅是可选的,那么对语言会有什么不良影响吗?或者对任何有效的程序都会有任何影响吗?
Ton*_*roy 38
假设我正在编写一个库并且在那里有一个当前返回常量的函数:
awesome_lib.h:
inline int f() { return 4; }
Run Code Online (Sandbox Code Playgroud)
如果constexpr不是必需的话,作为客户端代码的作者,您可能会离开并执行以下操作:
client_app.cpp:
#include <awesome_lib.h>
int my_array[f()];
Run Code Online (Sandbox Code Playgroud)
然后我应该改变f()说从配置文件返回值,你的客户端代码会破坏,但我不知道我冒着破坏你的代码的风险.实际上,可能只有当你遇到一些生产问题并重新编译时才发现这个额外的问题会让你的重建受挫.
只改变实施的f(),我会有效地改变可能被作出的使用界面.
相反,C++ 11以后提供,constexpr所以我可以表示客户端代码可以合理期望函数剩余a constexpr,并使用它本身. 我知道并认可这种用法是我界面的一部分. 正如在C++ 03中一样,编译器继续保证客户端代码不会依赖于其他非constexpr函数来构建,以防止上面的"不需要/未知的依赖"场景; 这不仅仅是文档 - 它是编译时的执行.
值得注意的是,这延续了C++的趋势,即为预处理器宏的传统用途提供更好的替代方案(考虑#define F 4,以及客户端程序员如何知道lib程序员是否认为公平游戏要改变#define F config["f"]),以及他们众所周知的"邪恶"等因为在语言的命名空间/类范围系统之外.
我认为这里的混淆是由于constexpr没有主动确保有任何一组参数,其结果实际上是编译时const:相反,它要求程序员对此负责(否则标准中的§7.1.5/ 5)认为程序格式错误,但不要求编译器发出诊断信息).是的,这是不幸的,但它并没有消除上述效用constexpr.
所以,也许问题不应该是"有什么意义constexpr",而是"为什么我可以编译一个constexpr永远不能实际返回const值的函数?".答:因为需要进行详尽的分支分析,这可能涉及任意数量的组合.编译时和/或内存(甚至超出任何可以想象的硬件的能力)诊断成本可能过高.此外,即使在实际必须准确诊断此类情况时,编译器编写者(已经在C++ 11实现中已经足够忙)也是一种全新的蠕虫病毒.还会对程序产生影响,例如constexpr在执行验证时需要在函数内调用的函数的定义(以及函数调用的函数等).
同时,缺少的constexpr仍然禁止使用作为一个常量的值:在严格是对sans- constexpr侧.如上所述,这很有用.
constexpr防止int x[f()]缺乏const预防const X x; x.f();- 他们都确保客户端代码不会硬编码不需要的依赖
在这两种情况下,您都不希望编译器const[expr]自动确定-ness:
你不希望客户端代码调用const对象上的成员函数,当你已经预见到函数将演变为修改可观察值,打破客户端代码
如果您已经预期稍后在运行时确定它,则不希望将值用作模板参数或数组维度
它们的不同之处在于编译器强制const使用const成员函数中的其他成员,但不强制执行编译时常量结果constexpr(由于实际的编译器限制)
Pot*_*ter 16
当我按下 Clang作者Richard Smith时,他解释道:
constexpr关键字确实有效用.
它会影响函数模板特化实例化的时间(如果在未评估的上下文中调用constexpr函数模板特化可能需要实例化;对于非constexpr函数也是如此;因为对一个函数的调用永远不能成为常量的一部分表达).如果我们删除了关键字的含义,我们必须尽早实例化更多的特化,以防调用恰好是一个常量表达式.
它通过限制在翻译期间尝试评估所需的实现的函数调用集来减少编译时间.(这对于需要实现以进行常量表达式求值的上下文很重要,但如果这样的评估失败则不是错误 - 特别是静态存储持续时间对象的初始化器.)
这一切看起来并不令人信服,但如果你仔细研究细节,事情就会解决constexpr.在使用ODR之前,不需要实例化函数,这实际上意味着在运行时使用.功能的特殊之处constexpr在于它们可以违反此规则并且无论如何都需要实例化.
函数实例化是一个递归过程.实例化一个函数会导致它使用的函数和类的实例化,而不管任何特定调用的参数.
如果在实例化此依赖关系树时出现问题(可能花费很大),则很难吞下错误.此外,类模板实例化可能具有运行时副作用.
给定函数签名中依赖于参数的编译时函数调用,重载解析可能导致函数定义的实例化仅仅是对重载集中的函数定义的辅助,包括甚至不被调用的函数.这种实例化可能具有副作用,包括不良形式和运行时行为.
这是一个确定的角落案例,但如果你不要求人们选择加入constexpr函数,就会发生不好的事情.