资格转换(这里:在隐式对象参数上)不是身份转换,并且在重载决议排名中具有成本
成员函数void foo(B<T>& x) const 是const-qualified,而模板成员函数template<class F> void foo(F f) 不是。这意味着后者更适合隐式对象参数为 not const 的调用,根据 [over.ics.rank]/3.2.5:
标准转换序列 S1 是比标准转换序列更好的转换序列
标准转换序列S2 if
- [...],或者,如果不是这样,S1 和 S2 仅在其限定转换 ([conv.qual]) 上有所不同,并产生相似的类型 T1 和 T2,
分别,其中 T1 可以通过限定转换为 T2
转化。
和[over.match.best]/2.1
如果你 const-qualify 自动变量 b 在 main 中,将选择非模板重载:
// ^^^ `foo` overloads as in OP's example
B<int> a{};
B<int> const b{}
b.foo(a); // foo_B
如果您改为const-quality 模板成员函数foo,则非模板将与模板重载相同(隐式对象参数需要 const-qualification),在这种情况下,非模板根据[over.match.best]/2.4,模板函数被选为最佳可行重载。
如果您不希望特定的重载参与类型谓词的重载解析:删除它
但是,b 在实际应用中不能声明为 const,归结为复制,比如 const c(b);然后使用 c.foo(a)
当模板参数的类型模板参数的模板参数是B 类模板的特化时,您可以使用特征来删除模板成员函数:
#include <iostream>
#include <type_traits>
template <class T, template <class...> class Primary>
struct is_specialization_of : std::false_type {};
template <template <class...> class Primary, class... Args>
struct is_specialization_of<Primary<Args...>, Primary> : std::true_type {};
template <class T, template <class...> class Primary>
inline constexpr bool is_specialization_of_v{is_specialization_of<T, Primary>::value};
template <class T> class B {
public:
void foo(B<T> &x) const { std::cout << "foo_B" << std::endl; }
template <class F, typename = std::enable_if_t<!is_specialization_of_v<F, B>>>
void foo(F f) {
std::cout << "foo_F" << std::endl;
}
};
int main() {
B<int> a;
B<int> b;
b.foo(a);
b.foo([]() { ; });
return (0);
}
我们利用了 P2098R1 的 is_specialization_of 特征(请注意,这对于作为别名模板的模板参数有实现差异 - 有点未指定的 IIRC)。
请注意,使用这种方法,对于 B 的 另一个 特化的参数(而不是隐式对象参数的参数),任何重载都不可行。