7

考虑以下程序:

template<typename T>
constexpr int f() 
{
    T{}.i; // error if instantiated with [T = double]
    return 42;
}

constexpr void g(char);

using U = decltype( g( {f<double>()} ) );

据我了解,最后一行是一个错误,因为调用f<double>()是在大括号初始化程序中,即使f<T>返回 a int,也需要返回的值int来确定它是否可以char按预期缩小到 a g。这需要f用 实例化的定义double,这会导致错误。gcc 和 clang 都拒绝此代码。


但是,如果将 的定义g更改为接受int参数:

constexpr void g(int);

那么似乎不需要实例化 的定义f,因为缩小转换必须成功。事实上,gcc 接受了这一点,但 clang 仍然实例化fdouble拒绝代码。此外,如果f仅声明但未定义,clang 接受代码,这意味着不需要定义,也不应实例化。

我的推理是否正确,这是一个clang错误,还是需要实例化,这实际上是一个gcc错误?

4

1 回答 1

2

我认为这是CWG #1581 ,由P0859解决。

temp.inst/5说:

除非函数模板特化是声明的特化,否则当在需要函数定义存在的上下文中引用特化或如果定义的存在影响程序的语义时,会隐式实例化函数模板特化。

存在会影响程序的语义吗?

temp.inst/8说:

如果表达式 ([expr.const]) 需要对变量或函数进行常量求值,则认为变量或函数定义的存在会影响程序的语义,即使不需要对表达式进行常量求值或者如果常量表达式评估不使用定义。

是否需要通过表达式进行持续评估?

expr.const/15.6-7说:

如果满足以下条件,则需要一个函数或变量来进行常量评估:

  • 一个 constexpr 函数,它由一个可能是常量求值的表达式命名,或者
  • 一个变量,其名称显示为一个潜在的常量评估表达式,该表达式要么是 constexpr 变量,要么是非易失性 const 限定的整数类型或引用类型。

它是由一个可能是常量评估的表达式命名的吗?

expr.const/15.1-15.5说:

表达式或转换在以下情况下可能会被评估为常量:

  • 一个明显的常数评估表达式,
  • 一个潜在评估的表达式,
  • 一个花括号初始化列表的直接子表达式,
  • 出现在模板化实体中的 form & cast-expression 的表达式,或
  • 上述之一的子表达式不是嵌套未计算操作数的子表达式。

它是一个花括号初始化列表的直接子表达式。

于 2020-08-24T05:42:55.130 回答