Rum*_*rak 9 c++ templates clang language-lawyer constexpr
下面的代码使用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() {}
Run Code Online (Sandbox Code Playgroud)
clang实例化函数体并绊倒了static_assert
.gcc和MSVC只是查看函数声明并忽略static_assert
正文.
如果删除constexpr,所有编译器都可以编译代码.
问题:
如果声明了返回类型,是否允许decltype查看函数体?
我正在寻找标准中相应部分的参考.
历史:如评论中所述,此问题作为CWG 问题 1581提出。在isocpp.org 线程中,Columbo 认为代码是有效的,因为模板从未在未计算的操作数中实例化,但是在 clang 错误报告中,“rsmith”反驳说某些decltype
表达式确实需要模板实例化。
clang 线程通过提出他们自己的(非标准)标准来临时解决这个问题,以决定何时decltype
实例化constexpr
模板。从 4.0 版开始,clang 确实成功编译了代码。
WG21的理查德·史密斯已经开始着手解决这一问题截至11月2017,与P0859。这向 [expr.const] 添加了新文本,它实现了上面讨论的 clang 行为:
一个表达式是潜在的常量,如果它是:
- 一个潜在的评估表达式([basic.def.odr]),
- 一个约束的表达,包括从所形成的一个约束的逻辑或表达的需要子句,
- 花括号初始化器列表的直接子表达式[脚注:可能需要不断求值以确定是否执行缩小转换 ([dcl.init.list])。],
- 在模板化实体中出现的形式转换
&
表达式的表达式[脚注:可能需要持续评估以确定此类表达式是否依赖于值 ([temp.dep.constexpr])。], 或者- 上述之一的子表达式不是嵌套未计算操作数的子表达式。
如果满足以下条件,则需要一个函数或变量来进行常量评估:
- 由表达式 ([basic.def.odr]) 命名的 constexpr 函数,该表达式可能是常量求值,或
- 一个变量,其名称显示为潜在的常量计算表达式,该表达式要么是 constexpr 变量,要么是非易失性 const 限定的整数类型或引用类型。
并且 [temp.inst] 被修改为如果模板特化会被实例化,如果它的定义影响程序的语义,这意味着它需要进行上面定义的持续评估,即使它实际上并不需要,可以这么说.
修改 ODR 是为了避免 Columbo 的反对。
该提案的建议更改确实出现在 N4727 中,这是一个 C++17 后的草案。所以我假设他们已经被接受,即使 P0859 链接和 CWG 缺陷列表还没有这么说。
在这些更改下,您的代码decltype(foo(1))
,表达式foo(1)
不是潜在的常量评估(因为它与上面的任何要点都不匹配),因此不得实例化模板和代码,并进行修改以避免 [dcl.constexpr] /6,应该编译成功。
(C++17 dcl.constexpr/6 说,如果没有有效的特化,模板是格式错误的 NDR;这对于 来说是正确的foo
,但是这可以通过添加来解决template <> constexpr auto wrong<float> = true;
,例如)。