绝对有可能,不需要
-复制/移动,
-改变班级,
-更改函数体(包括但不限于调用另一个类模板静态函数),
-改变函数的返回类型,
- 甚至使用一个常量表达式作为函数的返回类型/s!
解决方案
只需要将通用函数作为一个函数模板,它采用SFINAE技术以默认模板类型参数的形式用于额外的模板类型参数,以避免特殊情况并具有专业化/s
正常功能/秒:
template <typename T, typename = std::enable_if_t<!std::is_base_of_v<A, T> > >
void foo(const T& t) {
printf("T");
}
void foo(const A& t) {
printf("A");
}
说明:
对于一般情况,默认模板类型参数
std::enable_if_t<!std::is_base_of_v<A, T> > >
可以从第一个模板类型参数T 推导出来。因为存在且定义明确,所以会调用函数模板。
当使用基于类A 的类型的对象调用函数时,因为未定义std::enable_if_t<!std::is_base_of_v<A, T> > >,默认模板类型参数不存在,因此无法推断模板类型参数。所以编译器会寻找另一个具有相同名称和相似参数类型的函数,所以正常的函数
void foo(const A& t) { printf("A");}
会被调用,不会有歧义。
使用注意事项
对于新的专业化,只需向(一个)函数模板添加一个类似的伪类,并为新的专业化编写一个函数(非模板)。
如果模板默认类型参数看起来很大且令人困惑,可以简单地创建一个策略模板并使用它来代替。喜欢:
template<typename T, typename P>
using exclude = std::enable_if_t<!std::is_base_of_v<P, T> >;
template <typename T, typename = exclude<T,A> >
void foo(const T& t) {
printf("T");
}
另外由于在 C++17 之前没有启用某些功能,对于较低的 C++ 版本,可以编写如下模板:
template <typename T, typename = typename std::enable_if<!std::is_base_of<A, T>::value>::type>
另外,如果选择使用@songyuanyao的解决方案,它使用常量表达式作为函数的返回类型,如果函数的返回类型不是void,例如@ 987654332@,
解决方案变成这样:
template <typename T>
std::enable_if_t<!std::is_base_of_v<A, T>, return_type> foo(const T& t) {
printf("T");
return_type return_value;
return return_value;
}
template <typename T>
std::enable_if_t<std::is_base_of_v<A, T>, return_type> foo(const T& t) {
printf("A");
return_type return_value;
return return_value;
}
进一步的例子
最后,为了更好地理解SFINAE,可以考虑一种不需要任何库的不一般正确/不包罗万象的替代解决方案:
template<bool>
struct ifnot;
template<>
struct ifnot<false> {
enum {v};
};
template<typename T, typename P>
struct test {
static T value_of_T();
static char check(...);
static int check(P);
enum {v = sizeof(check(value_of_T())) - 1};
};
template <typename T, bool = ifnot<test<T, A>::v>::v>
void foo(const T& t) {
printf("T");
}
void foo(const A& t) {
printf("A");
}
虽然此解决方案也适用于此特定示例,但请注意此解决方案并不总是正确的。因为它只测试 T 到 A 的转换(即使它本身也不完整且有问题)而不是继承。特别是对于这些应该用类似类型的对象调用的函数,很有可能这些类型中的许多类型可以相互转换!
我认为继承测试的正确方法包括非相互转换测试和确定是否没有类型是void*。综合考虑 使用 std::is_base_of 或 std::is_base_of_v 要好得多。不过struct ifnot 是可以的,甚至可以用std::enable_if 换取它,并适当更改它们的用法。
祝你好运!