应该decltype(foo(1))实例化constexpr函数模板foo吗?

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查看函数体?

我正在寻找标准中相应部分的参考.

M.M*_*M.M 5

历史:如评论中所述,此问题作为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;,例如)。