Constexpr构造函数无法满足要求,但仍为constexpr。为什么?

时间:2018-12-05 12:40:38

标签: c++ language-lawyer c++17

该标准在dcl.constexpr/6中说明了模板constexpr函数/构造函数:

  

如果constexpr函数模板或类模板的成员函数的实例化模板特化将无法满足constexpr函数或constexpr构造函数的要求,则即使调用了constexpr函数或constexpr构造函数,该专业化仍然是constexpr函数或constexpr构造函数。这样的函数不能出现在常量表达式中。如果当模板的特殊化都不能满足constexpr函数或constexpr构造函数的要求,则认为模板是格式错误的,不需要诊断。

有趣的部分是:

  

无法满足... constexpr构造函数的要求,该专业化仍然是... constexpr构造函数

因此,即使构造函数标记有constexpr,也可能无法在常量表达式中使用它。

为什么存在此规则?当函数不满足要求时为什么不删除constexpr

当前的行为有两种不良情况:

  • 非constexpr-ness不会在最接近的可能位置捕获,而是在使用它的实际constexpr表达式中捕获。因此,我们必须找到有问题的部分,其中constexpr被无声删除了。
  • 打算静态初始化的对象(因为它有一个constexpr构造函数)将被dynamically initialized进行任何错误/警告(因为该构造函数不是“真正的” constexpr) 。

此规则是否有一些优点,可以平衡其缺点?

2 个答案:

答案 0 :(得分:5)

此规则允许您编写模板化的构造函数/函数,并将其标记为constexpr,即使并非总是constexpr(至少有时)也是如此。

例如,std::pair具有constexpr constructors,但是它当然可以在常量表达式之外使用。

这是非常明智的,因为否则,即使代码完全相同,也必须重复所有这些功能(一次使用constexpr,一次不使用)。我们甚至不要考虑歧义。

由于通常无法证明模板永远无法满足constexpr,因此无需对其进行诊断(但它的格式不正确,因此编译器如果可以在给定的情况下证明这一点,则可以向您投诉)

您是正确的,如果您想指定“此函数只能在常量表达式中使用”,这不是很有用,但这不是此措辞的目的。

编辑:为明确起见,constexpr 对于函数仅表示“在常量表达式中求值的合法条件” (更精确的措词here) ,不能 “只能在编译时进行评估” 。相反,constexpr变量必须使用常量表达式初始化。


另一种编辑:感谢@JackAidley,我们有确切的措词可以讨论!

  

如果constexpr函数模板的实例化模板特化将无法满足constexpr函数的要求,   constexpr说明符将被忽略,并且特殊化不是constexpr函数。

与此相关的问题是“至少有一组参数可以对其进行常量求值”是“ constexpr函数的要求”的一部分。因此,编译器无法实现此子句,因为无法(通常)证明给定功能(或功能模板实例化)是否存在这样的集合。您要么必须进一步弄清这一要求,要么就此放弃。看来委员会选择了后者。

答案 1 :(得分:4)

earlier versions条关于更改语言的建议中,它按照您的建议进行操作:

  

如果constexpr函数的实例化模板特化   模板将无法满足constexpr的要求   函数,constexpr指定符将被忽略,专业化为   不是constexpr函数。

但是后来改变了。我无法找到对您问题的任何明确答案,但我认为可以合理地相信答案是constexpr对代码进行了其他语义更改,并且即使该功能不再可用,这些更改也得以保留在其他constexpr条语句中。如果您查看defect report 1358,其中包括对当前措词的更改,您会看到一种中间形式的单词,其中包括有关保持const状态的注释。

我还认为,尽管保留constexpr的身份并不直观,但您对它的两个论点都是错误的:

  1. 在进行模板实例化时捕获constexpr违反了C ++模板通常的工作方式-当您尝试使用无法用于该类型的模板时,只会收到错误;只是不能完成整个签名不是错误。为constexpr引入特殊情况机制会不必要地造成混淆,并限制实用性,因为您现在需要为constexpr和非constexpr类型编写不同的模板。

  2. 因为保持constexpr的说明符,后退不是针对一般运行时动态初始化,而是针对static初始化时的动态初始化。可能由于静态初始化顺序失败而导致问题,但至少在输入main()函数之前发生。