问题描述
请考虑以下程序:
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>
返回了int
,返回的{需要{1}}来决定是否可以按照int
的预期将其缩小到char
。这要求使用g
实例化f
的定义,这会导致错误。 gcc和clang都拒绝此代码。
但是,如果将double
的定义更改为接受g
参数:
int
然后,似乎不需要实例化constexpr void g(int);
的定义,因为缩小的转换必须成功。确实,gcc接受了这一点,但是clang仍然使用f
实例化f
并拒绝了code。另外,如果仅声明double
而不进行定义,则clang接受该代码,这意味着该定义是不需要的,并且不应实例化。
我的推理是否正确,这是一个clang错误,还是需要实例化,而这实际上是gcc错误?
解决方法
除非功能模板专长是已声明的专长,否则当在需要存在函数定义的上下文中引用该专长或如果定义的存在影响程序的语义时,将隐式实例化功能模板专长。 / p>
存在会影响程序的语义吗?
如果通过表达式([expr.const])进行常量求值需要变量或函数,则即使存在表达式的常量求值,也认为存在变量或函数的定义会影响程序的语义。不需要,或者如果常量表达式评估不使用该定义。
通过表达式进行常量评估是否需要?
一个函数或变量需要常量评估,如果它是:
- 一个constexpr函数,该函数由一个表达式(该表达式的值可能为常量)或
- 其名称显示为可能为常量的表达式的变量,该表达式可以是constexpr变量,也可以是非易失性const限定整数类型或引用类型。
它是由一个可能被常量求值的表达式命名的吗?
如果表达式或转换满足以下条件,则可能是常量:
- 一个明显为常量值的表达式
- 可能评估的表达式
- 括号初始列表的直接子表达式
- 出现在模板实体中的形式为&cast-expression的表达式,或者
- 上述之一的子表达式,不是嵌套的未求值操作数的子表达式。
它是braced-init-list的直接子表达式。