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

12
以下代码在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查看函数体?

我正在寻找标准中相应部分的参考文献。


8
我认为更好的提问方式是:decltype是否会触发模板实例化? - Ryan Haining
2
@BoPersson编译器在不实例化主体的情况下无法判断。可能存在一个wrong的特化是true的情况。 - Rumburak
在大多数情况下,它不会。正如问题中所写的那样:如果您删除constexpr,则没有编译器实例化该函数体。 - Rumburak
4
如果一段代码只有函数模板的声明而没有定义,但是可以编译通过,那么根据"[temp.inst]/3",函数模板特化会在需要存在函数定义的上下文中隐式实例化。这似乎意味着,如果一个代码片段只需使用函数模板的声明就能编译通过,而不需要函数模板的定义,则该代码片段不应触发隐式实例化。然而,Clang违反了这个规则 - 可以仅使用声明编译,但使用定义时会失败。在我这个外行人的眼中看来,这似乎是一个错误。 - Igor Tandetnik
1
在搜索clang错误数据库时,我发现这个主题存在一个未解决的核心问题:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1581 - Rumburak
1个回答

5

历史: 如评论所述,此问题是在CWG issue 1581中提出的。在isocpp.org thread中,Columbo认为代码是有效的,因为模板永远不会在未求值的操作数中实例化,然而on a clang bug report,“rsmith”反驳说一些decltype表达式确实需要模板实例化。

Clang线程通过提出自己的(非标准)标准来临时解决了这个问题,判断何时decltype将实例化一个constexpr模板。从4.0版本开始,Clang成功编译了这段代码。


Richard Smith于2017年11月开始着手解决此问题,使用P0859增加了新的文本到[expr.const]中,实现了如上所述的clang行为:

如果表达式满足以下条件,则该表达式是潜在常量求值

  • 一个潜在求值表达式([basic.def.odr]),
  • 一个约束表达式,包括由requires-clauseconstraint-logical-or-expression形成的约束表达式,
  • 大括号初始化列表的直接子表达式[注脚:可能需要常量求值来确定是否执行了缩小转换([dcl.init.list])],
  • 模板实体内发生的形式为& cast-expression的表达式[注脚:可能需要常量求值来确定这种表达式是否具有值依赖性([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;来修复这个问题)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接