【问题标题】:Should decltype(foo(1)) instantiate the constexpr function template foo?decltype(foo(1)) 应该实例化 constexpr 函数模板 foo 吗?
【发布时间】:2017-03-28 08:55:34
【问题描述】:

以下代码使用 gcc 和 MSVC 编译,但使用 clang-3.5 和当前主干测试的 clang 失败)。

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

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

【问题讨论】:

  • 我认为更好的方法是:decltype 触发模板实例化吗?
  • @BoPersson 如果不实例化主体,编译器无法判断。 wrong 可能有一个特化,即 true
  • @RyanHaining 在大多数情况下,它不会。正如问题中所写:如果删除constexpr,则没有编译器实例化主体。
  • "[temp.inst]/3 ...当在需要函数定义存在的上下文中引用特化时,函数模板特化被隐式实例化。"这似乎表明,如果一段代码仅使用可用函数模板的声明而不是定义进行编译,那么该段代码不应触发隐式实例化。 Clang 违反了这个不变量 - 工作 with declaration alone,失败 with definition。在我未经训练的眼睛看来,这就像一个错误。
  • 搜索clang bug库,得知该主题有一个未解决的核心问题:open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1581

标签: c++ templates clang language-lawyer constexpr


【解决方案1】:

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

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


WG21 的 Richard Smith 已于 2017 年 11 月开始通过P0859 解决该问题。这会在 [expr.const] 中添加新的文本,实现上面讨论的 clang 的行为:

如果一个表达式是可能是常量

  • 潜在评估的表达式 ([basic.def.odr]),
  • 一个constraint-expression,包括一个由requires-clauseconstraint-logical-or-expression构成的,
  • braced-init-list 的直接子表达式[ 脚注: 可能需要常量评估来确定是否执行了缩小转换([dcl.init.list ])。 ],
  • &amp; 形式的表达式 cast-expression 出现在模板化实体中[脚注: 可能需要常量评估来确定这样的表达式是否为值-依赖([temp.dep.constexpr])。 ],或
  • 上述之一的子表达式不是嵌套未计算操作数的子表达式。

如果是,需要一个函数或变量来进行常量评估

  • 一个 constexpr 函数,由一个表达式 ([basic.def.odr]) 命名,该表达式可能是常数计算,或者
  • 一个变量,其名称显示为一个潜在的常量计算表达式,该表达式要么是 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 &lt;&gt; constexpr auto wrong&lt;float&gt; = true; 来解决此问题) .

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-09
    相关资源
    最近更新 更多