为什么重载解析更喜欢不受约束的模板函数而不是更具体的模板函数?
Why is Overload Resolution favoring unconstrained template function over a more specific one?
>我有一个乘法的最小表达式模板库,即
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>
类型的变量乘以某种类型T
的A<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<A<double>>
相比transpose<B<double>>
是一种完全不同的类型,因此重载分辨率会选择operator*
的不受约束的模板版本。
(当然,如果我将main
中的变量定义更改为A
而不是B
,ADL 调用专用函数,输出是called: T operator*(const A<T> &one, const A<T> &two)
和6
)。
我最近了解了 SFINAE,所以我预计对更具体的乘法运算符进行以下更改会导致重载 resulution 选择专用函数:
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
,则启用此功能,以便A<T>
是V
的基类。存在量化是一个很好的指标,表明你所要求的不能是SFINAEd。
如果您禁用不受约束的模板,您可以自己看到这一点,就像我在这里所做的那样。这会强制编译器详细说明为什么不允许使用其他函数。
您可以通过A
(因此B
)类提供T
来解决此问题,如下所示:
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;
}
[现场示例]
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 为什么虚函数不能是静态的和全局的?
- g++ 说函数不存在,即使包含正确的标头
- 当调用switch语句中的函数时(即使函数不包含循环),似乎是永不结束的循环的问题
- IpOpt拒绝解决不受约束的问题
- C++Brute Force攻击函数不会返回结果
- 仅包含可移动 std::map 的类的移动构造函数不起作用
- 为什么我的数组双精度函数不起作用?
- 受约束的成员函数和显式模板实例化
- std::unordered_map析构函数不释放内存?
- 函数不接受 X 参数,函数使用默认参数
- 为什么用户定义的函数不按照给定的顺序对相同长度的元素进行排序?
- 为什么继承的受保护构造函数不能公开?
- 为什么重载解析更喜欢不受约束的模板函数而不是更具体的模板函数?
- 不受控制的循环和函数 C++跳过
- 函数不受主内存约束的函数所需的复杂性是什么?
- 父级上受保护的构造函数和继承的默认构造函数不受保护
- 受保护的函数不需要虚拟
- 未定义的行为-是用其他语言编写的函数,受C++关于UB的规则的约束
- 类层次结构中的受约束构造函数,C++