如果有一些不相关的类及其对象,它们并没有继承关系,但是可能拥有相同的接口定义,如何对这些对象在“运行时进行多态处理”?
0. 鸭子类型
鸭子类型是一种动态类型语言中的类型推断风格,根据对象的行为而不是类型来判断对象的特征。
“如果它走路像鸭子,叫声像鸭子,那么它一定就是一只鸭子。”
1. std::variant
std::variant是C++17引入的可以表示“多种类型”的类模版。一个std::variant类似于一个union对象,可以存储一个在类模版定义时指定的一种类型的对象。
使用std::variant来实现运行时多态,前提是事先可以明确一组类型。
using Anim = std::variant<Cat, Dog>; // Anim对象表示一个Cat或Dog
Anim a1 = Cat();
Anim a2 = Dog();
2. std::visit
std::visit可以调用std::variant中全部类型都拥有的“同名的函数”(公共接口)。std::visit的使用需要指定一个visitor lambda,来具体调用公共接口。
auto getSay = [](auto &anim){return anim.say();};
return std::visit(getSay, a1); // 仅当Cat和Dog中都有say()方法,编译才会通过。
3.🌰例子
#include <iostream>
class Dog {
public:
void swim() const {
std::cout << "Dog can swim" << std::endl;
}
void say() const {
std::cout << "Wang~" << std::endl;
}
};
class Cat {
public:
void climbTree() const {
std::cout << "Cat can climb trees" << std::endl;
}
void say() const {
std::cout << "Meow Meow~~~" << std::endl;
}
};
using Anim = std::variant<Cat, Dog>;
int main() {
// 对象可以具体是variant声明时输入的类型的某一种
Anim dog = Cat();
// 可以通过std::visit来具体调用variant中全部类型都拥有的“同名的函数”(公共接口)
auto getSay = [](const auto &anim){ return anim.say();};
std::visit(getSay, dog);
return 0;
}