下面的代码可以在gcc和MSVC下编译,但是在使用clang(我用clang-3.5和当前主干测试)时失败。
template <typename T>
constexpr auto wrong = false;
template <typename T>
constexpr auto foo(const T t) -> int
{
static_assert(wrong<T>, "");
return {};
}
using F = decltype(foo(1));
int main() {}
clang实例化了函数体并绊倒了static_assert
。gcc和MSVC只看函数声明,忽略函数体中的static_assert
。
如果你去掉constexpr,所有的编译器都能很好地编译代码。
问题:
如果声明了返回类型,是否允许decltype查看函数体?
我正在寻找标准中相应部分的参考。
历史记录:如评论所述,此问题作为CWG第1581号问题提出。在isocpp.org线程中,Columbo认为代码是有效的,因为模板永远不会在未求值的操作数中实例化,然而在clang错误报告中,"rsmith"反驳说一些decltype
表达式确实需要模板实例化。
decltype
何时实例化constexpr
模板。从4.0版本开始,clang可以成功编译代码。
WG21的Richard Smith在2017年11月开始解决这个问题,P0859。这将向[expr]添加新的文本。Const],它实现了上面讨论的clang的行为:
表达式是潜在的常量,如果它是:
- 潜在求值表达式([basic.def.odr]),
- 一个约束表达式,包括由require子句的约束逻辑或表达式组成的约束表达式,
- 带括号的初始化列表的直接子表达式[脚注:常数求值可能需要确定是否执行窄化转换([dcl.init.list])。),
- 在模板实体中出现的形式为
&
cast-expression的表达式[脚注:常量计算可能需要确定该表达式是否依赖于值([temp.dep.constexpr])。),或- 不是嵌套的未求值操作数的子表达式。
如果满足以下条件,则函数或变量需要用于常量求值
- 一个constexpr函数,由一个表达式([basic.def.odr])命名,该表达式可能是常量求值,或者
- 一个变量,其名称显示为一个潜在的常量求值表达式,该表达式要么是constexpr变量,要么是非易失性的const限定整型,要么是引用类型。
和[temp.inst]被修改为,如果一个模板专门化的定义影响了程序的语义,那么它将被实例化,这意味着它是需要用于上面定义的常量求值,即使它实际上并不需要,可以这么说。
修改了ODR以避免colombo的反对。
该提案的建议更改确实出现在N4727中,这是一个后c++ 17草案。所以我认为他们已经被接受了,即使P0859链接和CWG缺陷列表还没有这么说。
在这些更改下,您的代码decltype(foo(1))
,表达式foo(1)
是不潜在的常数计算(因为它不匹配上面的任何项目符号),所以模板必须不被实例化,并且代码,修改以避免[dcl]。/6, 应该编译成功
(c++ 17 dcl。constexpr/6表示,如果没有有效的专门化,则模板是格式不良的NDR;这对于foo
是正确的,但是这可以通过添加template <> constexpr auto wrong<float> = true;
(例如)来修复。