使用模板对类层次结构进行函数重载的这种方式安全吗
Is this way of function overloading for class hierarchies using templates safe?
好吧,这有点复杂,所以请耐心等待。:)
我们有这样一个简单的类层次结构:
class A {};
class DA : public A {};
class DDA : public DA {};
我们在这些类上运行以下函数:
void f(A x) {
std::cout << "f A" << std::endl;
}
void f(DA x) {
std::cout << "f DA" << std::endl;
}
void f(DDA x) {
std::cout << "f DDA" << std::endl;
}
现在,我们想添加另一个函数,以稍微不同的方式处理DA。
(1) 第一次尝试可能是这样的:
void g(A t) {
std::cout << "generic treatment of A" << std::endl;
std::cout << "called from g: ";
f(t);
}
void g(DA t) {
std::cout << "special treatment of DA" << std::endl;
std::cout << "called from g: ";
f(t);
}
但是用每个类的对象调用它显然没有达到预期的效果。
呼叫:
A a; DA b; DDA c;
g(a); g(b); g(c)
结果:
generic treatment of A
called from g: f A
special treatment of DA
called from g: f DA
special treatment of DA
called from g: f DA //PROBLEM: g forgot that this DA was actually a DDA
(2) 因此,我们可能会尝试使用模板:
template<typename T>
void h(T t) {
std::cout << "generic treatment of A" << std::endl;
std::cout << "called from h: ";
f(t);
}
template<>
void h<>(DA t) {
std::cout << "special treatment of DA" << std::endl;
std::cout << "called from h: ";
f(t);
}
结果是:
generic treatment of A
called from h: f A
special treatment of DA
called from h: f DA
generic treatment of A //PROBLEM: template specialization is not used
called from h: f DDA
那么,我们不使用模板专门化,而是为特殊情况定义一个非模板函数,怎么样?(关于这个非常令人困惑的问题的文章。)事实证明,它的行为方式完全相同,因为根据文章的说法,非模板函数是"一等公民",但似乎失败了,因为使用它需要类型转换。如果要使用它,那么我们只会回到第一个解决方案(我想),它会忘记DDA的类型。
(3) 现在我在工作中遇到了这个代码,它对我来说似乎很新奇:
template<typename T>
void i(T t, void* magic) {
std::cout << "generic treatment of A" << std::endl;
std::cout << "called from i: ";
f(t);
}
template<typename T>
void i(T t, DA* magic) {
std::cout << "special treatment of DA" << std::endl;
std::cout << "called from i: ";
f(t);
}
但它似乎正是我想要的:
generic treatment of A
called from i: f A
special treatment of DA
called from i: f DA
special treatment of DA
called from i: f DDA
尽管它需要以一种奇怪的方式调用:i(a,&a);i(b,&b);i(c,&c);
现在我有几个问题:
- 为什么这样做
- 你认为这是个好主意吗?可能的陷阱在哪里
- 你还建议用哪种方法来完成这种专业化
- (类型转换如何适应模板偏序之类的疯狂…)
我希望这是相当清楚的
函数模板重载
template<typename T>
void i(T t, DA* magic) {
仅当参数CCD_ 1可转换为类型CCD_。这显然是&b
的情况,但也是&c
的情况,因为派生的指针可以转换为基指针。void *
功能模板过载始终可用,但根据§13.3.3.2:4:,DA *
优先于void *
c++11
13.3.3.2对隐式转换序列进行排序[over.ics.rank]
[…]
4标准转换序列按等级排序:精确匹配比晋升,这是比转换更好的转换。具有相同秩的两个转换序列除非以下规则之一适用,否则无法区分:
[…]
--如果
B
类是直接或间接从A
类派生的,则magic
0到A*
的转换比转换好B*
到void*
的转换以及A*
到void*
的转换优于B*
到void*
的转换。
正如你所指出的,这是一个完全可行的方案;将魔术封装在另一个模板函数中会更有意义,该函数负责用(a, &a)
:调用i
template<typename T>
void j(T t) {
i(t, &t);
}
就安全性而言,这很好;如果CCD_ 20过载丢失,则CCD_;这是否可取由你来决定。
作为替代方案,您可以使用DA *
2在模板之间进行选择:
template<typename T>
typename std::enable_if<!std::is_base_of<DA, T>::value>::type g(T t) {
std::cout << "generic treatment of A" << std::endl;
f(t);
}
template<typename T>
typename std::enable_if<std::is_base_of<DA, T>::value>::type g(T t) {
std::cout << "special treatment of DA" << std::endl;
f(t);
}
相关文章:
- 继承函数的重载解析
- 你能重载对象变量名本身返回的内容吗
- 从父命名空间重载类型
- 重载方法的方式会在使用临时调用时生成编译器错误
- C++:使用 std::unique_ptr 访问重载运算符++的最佳方式?
- 重载 == 以递归方式比较两个链表
- 重载运算符是具有较小默认对齐方式的新增运算符
- 重载小于 (<) 运算符,用于以多种方式对对象进行排序
- 重载输入/输出运算符,为什么它以这种方式工作而不是以另一种方式工作
- 从BoostCon谈话中重载取消引用运算符的奇怪方式
- 使用模板对类层次结构进行函数重载的这种方式安全吗
- 为什么这两个重载函数的声明方式不同
- C++等于(==)重载,比较所有属性的快捷方式或最佳方法
- 运算符>>(,) 重载以令人惊讶的方式运行
- QT - QAction::eventFilter:不明确的快捷方式重载
- 为什么我的重载模板函数提升为const的方式与非模板函数不同?
- 重载函数的不同方式
- Java和c++中重载方法的继承在基本方式上有所不同
- C++ 减少运算符重载,使用哪种方式
- c++以一种有效的方式重载plus