c++ - 为什么重载解决方案偏爱不受约束的模板功能而不是更具体的功能?
问题描述
我有这个带有乘法的最小表达式模板库,即
template <typename T, typename U>
struct mul {
const T &v1;
const U &v2;
};
template <typename T, typename U>
mul<T, U> operator*(const T &one, const U &two) {
std::cout << " called: mul<T, U> operator*(const T &one, const T &two)\n";
return mul<T, U>{one, two};
}
和转置,即
template <typename T>
struct transpose {
const T &t;
};
template <typename T>
transpose<T> tran(const T &one) {
return transpose<T>{one};
}
我将介绍一些类型A
和B
,其中后者是前者的子类:
template <typename T>
struct A {
T elem;
};
template <typename T>
struct B : A<T> {
B(T val) : A<T>{val} {}
};
然后,我可以按如下方式调用我的表达式模板库(带有用于打印到的重载std::cout
):
template <typename T, typename U>
std::ostream &operator<<(std::ostream &os, const mul<T, U> &m) {
os << " unconstrained template \n";
}
int main(int argc, char const *argv[]) {
B<double> a{2};
B<double> b{3};
std::cout << tran(a) * b << "\n";
return 0;
}
这给了我输出:
called: mul<T, U> operator*(const T &one, const T &two)
unconstrained template
到现在为止还挺好。现在假设我想要一个专门的处理方法来处理“类型变量的转置A<T>
乘以A<T>
某种类型的类型变量T
”,就像我在main
. 为此,我将介绍
template <typename T>
T operator*(const transpose<A<T>> &one, const A<T> &two) {
std::cout << " called: T operator*(const A<T> &one, const A<T> &two)\n";
return one.t.elem * two.elem;
}
我运行与main
上面相同的函数,我仍然得到与上面相同的输出(unconstrained template
)。这是意料之中的,因为transpose<B<double>>
与 相比是完全不同的类型transpose<A<double>>
,所以重载决议选择了operator*
.
(当然,如果我将变量定义更改main
为A
而不是B
,ADL 会调用专用函数并且输出是called: T operator*(const A<T> &one, const A<T> &two)
and 6
)。
我最近了解了 SFINAE,因此我预计对更具体的乘法运算符的以下更改会导致重载结果以选择专用函数:
template <typename T, typename V>
std::enable_if_t<std::is_base_of<A<T>, V>::value, T> operator*(const transpose<V> &one,
const V &two) {
std::cout << " called: std::enable_if_t<std::is_base_of<A<T>, V>::value, T> operator*(const "
"transpose<V> &one, const V &two)\n";
return one.t.elem * two.elem;
}
即使使用 SFINAE'doperator*
我仍然得到unconstrained template
版本。怎么会?我应该进行哪些更改来调用更专业的模板函数?
解决方案
问题是在 SFINAE 重载中,T
是在非推导上下文中使用的。您实际上是在询问编译器:“如果存在.的基类,T
请启用此功能。” 存在量化是一个很好的指标,表明你所要求的不能是 SFINAEd。A<T>
V
如果您禁用不受约束的模板,您可以自己看到这一点,就像我在此处所做的那样。这迫使编译器说明为什么另一个函数是不可接受的。
T
您可以通过您的A
(因此B
)类来解决这个问题,如下所示:
template <typename T>
struct A {
using Type = T;
T elem;
};
template <typename V>
std::enable_if_t<std::is_base_of<A<typename V::Type>, V>::value, typename V::Type> operator*(const transpose<V> &one,
const V &two) {
std::cout << " called: std::enable_if_t<std::is_base_of<A<T>, V>::value, T> operator*(const "
"transpose<V> &one, const V &two)\n";
return one.t.elem * two.elem;
}
推荐阅读
- c# - 如何在 c# 中将方法名称作为字符串获取?
- kubernetes - 本地机器上的 Docker Desktop Kubernetes 身份验证
- r - 如何使用 R 获得 RCBD 的方差分析?
- jenkins - How to prevent Jenkins to get the entire TFS workspace?
- javascript - 当我运行下面的 javascript 时没有输出,我是新手,但我想要使用谷歌地图 API 的网站
- c# - C# : Find all points on an arc formed by 3 points (3D)
- c# - Blazor 身份验证和授权
- python - 修改一段代码以允许浮点数和负数以及输入字符串中字符之间的任意数量的空格
- python - 使用条件从 pandas df 创建多个列表
- sql - 每行中有太多空字段会影响 PostgreSQL 中的搜索性能吗?