C++模板选择 - 不寻常的情况

C++ template selection - unusual case

本文关键字:不寻常 情况 选择 C++      更新时间:2023-10-16

>我有以下定义

template <typename T1, typename T2> class ArithmeticType {
public:
T1 x1;
T2 x2;
typedef typeof(x1+x2) Type;
};
template <typename D1, typename D2> inline
std::complex<typename ArithmeticType<D1,D2>::Type>
operator+(const std::complex<D1>& x1, const std::complex<D2>& x2) {
D1 x1r = real(x1);
D1 x1i = imag(x1);
D2 x2r = real(x2);
D2 x2i = imag(x2);
return std::complex<typename ArithmeticType<D1,D2>::Type>(x1r+x2r, x1i+x2i);
}

然后我按如下方式使用此类

std::complex<double> x;
std::cout << typeid(x).name() << std::endl;
ArithmeticType<std::complex<double>,std::complex<float>>::Type y;
std::cout << typeid(y).name() << std::endl;

产生输出

St7complexIdE
St7complexIdE

这对我来说很有意义:y属于std::complex<double>

现在,通过添加复杂和浮动类型的缩进,我添加了operator+模板的专用化,以便代码成为

template <typename T1, typename T2> class ArithmeticType {
public:
T1 x1;
T2 x2;
typedef typeof(x1+x2) Type;
};
template <typename D1, typename D2> inline
std::complex<typename ArithmeticType<D1,D2>::Type>
operator+(const std::complex<D1>& x1, const std::complex<D2>& x2) {
D1 x1r = real(x1);
D1 x1i = imag(x1);
D2 x2r = real(x2);
D2 x2i = imag(x2);
return std::complex<typename ArithmeticType<D1,D2>::Type>(x1r+x2r, x1i+x2i);
}
template <typename D1, typename D2> inline
std::complex<typename ArithmeticType<D1,D2>::Type>
operator+(const std::complex<D1>& x1, const D2 x2) {
D1 x1r = real(x1);
D1 x1i = imag(x1);
return std::complex<typename ArithmeticType<D1,D2>::Type>(x1r+x2, x1i+x2);
}

我运行与以前相同的代码,但现在出现编译错误,因为编译器尝试使用第二个专用化而不是第一个专用化。 这对我来说没有意义。 我认为(g++(编译器仍然会选择第一个专业化。

有人能解释为什么会发生这种情况的规则吗?

我相信你正在观察由cppreference/SFINAE的这句话引起的行为:

只有函数类型或其模板参数类型的直接上下文中的类型和表达式中的故障才是 SFINAE 错误。如果对替换类型/表达式的计算导致副作用,例如实例化某些模板专用化、生成隐式定义的成员函数等,则这些副作用中的错误将被视为硬错误

一个最小的例子:

template <typename T>
struct has_foo
{
using foo_t = decltype(T{}.foo());
};
template <typename T> void f(T&&) { }
// SFINAE error => no compilation error:
template <typename T> decltype(T{}.foo()) f(T&&) { }
// non-SFINAE error => hard error:
template <typename T> typename has_foo<T>::foo_t f(T&&) { }
int main()
{
f(1);
}

您的代码基本上表现相同,只是您ArithmeticTypehas_foo

而不是。

至于带有auto的返回类型扣除,请参阅,例如:SFINAE 与 C++14 返回类型扣除。

具有与上述相同has_foo的工作示例:

#ifndef DEFINE_THIS_SYMBOL_TO_CAUSE_COMPILATION_ERROR
void f(int) { }
#endif
// no SFINAE => viable candidate
template <typename T> auto f(T&&)
{
return typename has_foo<T>::foo_t{};
}
int main()
{
f(1);
}

如果定义了void f(int) { },则匹配效果更好,并且不会实例化模板化f

第二个模板的问题在于返回值的扣除:

template <typename D1, typename D2> inline
std::complex<typename ArithmeticType<D1,D2>::Type>
operator+(const std::complex<D1>& x1, const D2 x2)

当您尝试实例化ArithmeticType<std::complex<double>, std::complex<float> >

第二种operator+类型推导出为: D1:double, D2:std::complex<float>.然后,您尝试使用D1=doubleD2=std::complex<float>来实例化ArithmeticType。反过来,这又试图推断出doublestd::complex<float>operator+类型。没有这样的函数,你无法编译。