扩展类型擦除:C++17中同时约束类型具备飞行与打印能力的实现方法
扩展类型擦除:C++17中同时约束类型具备飞行与打印能力的实现方法
你好!要在C++17里实现同时约束类型具备飞行(fly)和打印(print)能力的类型擦除,核心思路就是把两个操作的抽象接口合并,再做对应的适配层。我来一步步给你拆解实现方法:
第一步:补全并实现Printing相关的类型擦除组件
先把你已经有的Flying相关代码补全,然后照着这个对称结构实现Printing的类型擦除组件:
#include <iostream> #include <memory> #include <type_traits> #include <utility> // 补全你已有的Flying相关代码 class FlyingConcept { public: virtual ~FlyingConcept() = default; virtual void fly() = 0; }; template <typename T> class FlyingModel : public FlyingConcept { public: FlyingModel(T t) : t{std::forward<std::decay_t<T>>(t)} {} void fly() override { t.fly(); } private: std::decay_t<T> t; }; class AnyFlyer { public: template <typename T> AnyFlyer(T t) : flyingConcept{std::make_unique<FlyingModel<std::decay_t<T>>>(std::forward<std::decay_t<T>>(t))} {} void fly() { flyingConcept->fly(); } private: std::unique_ptr<FlyingConcept> flyingConcept; }; // 新增Printing相关的类型擦除组件,和Flying结构完全对称 class PrintingConcept { public: virtual ~PrintingConcept() = default; virtual void print() = 0; }; template <typename T> class PrintingModel : public PrintingConcept { public: PrintingModel(T t) : t{std::forward<std::decay_t<T>>(t)} {} void print() override { t.print(); } private: std::decay_t<T> t; }; class AnyPrinter { public: template <typename T> AnyPrinter(T t) : printingConcept{std::make_unique<PrintingModel<std::decay_t<T>>>(std::forward<std::decay_t<T>>(t))} {} void print() { printingConcept->print(); } private: std::unique_ptr<PrintingConcept> printingConcept; };
第二步:实现同时支持飞行和打印的组合类型擦除
现在要把两个能力合并,核心是定义一个同时包含fly()和print()的抽象接口,再实现对应的适配模型,让它能包装任意同时具备这两个能力的类型:
// 组合抽象接口:同时要求fly和print两个操作的纯虚函数 class FlyingAndPrintingConcept { public: virtual ~FlyingAndPrintingConcept() = default; virtual void fly() = 0; virtual void print() = 0; }; // 组合模型:适配任意同时具备fly和print能力的类型T template <typename T> class FlyingAndPrintingModel : public FlyingAndPrintingConcept { public: // 用完美转发处理左值、右值等各种参数类型 template <typename U> FlyingAndPrintingModel(U&& t) : t_{std::forward<U>(t)} {} void fly() override { t_.fly(); } void print() override { t_.print(); } private: std::decay_t<T> t_; }; // 对外使用的统一类型:让用户无需关心内部细节,直接调用操作 class AnyFlyerPrinter { public: template <typename T> AnyFlyerPrinter(T&& t) : concept_ptr_{std::make_unique<FlyingAndPrintingModel<std::decay_t<T>>>(std::forward<T>(t))} { // C++17不能用concepts,用静态断言在编译期就检查类型是否符合要求 static_assert(std::is_invocable_r_v<void, decltype(&T::fly), T>, "传入的类型必须具备fly()方法"); static_assert(std::is_invocable_r_v<void, decltype(&T::print), T>, "传入的类型必须具备print()方法"); } // 对外暴露统一的操作接口 void fly() { concept_ptr_->fly(); } void print() { concept_ptr_->print(); } private: std::unique_ptr<FlyingAndPrintingConcept> concept_ptr_; };
第三步:测试代码验证功能
咱们写两个具备双能力的测试类,验证一下这个实现是否能正常工作:
// 测试用例1:会飞还会自我介绍的小鸟 class Bird { public: void fly() { std::cout << "小鸟扑棱着翅膀飞起来啦!\n"; } void print() { std::cout << "大家好~我是一只小麻雀!\n"; } }; // 测试用例2:会悬停还能报状态的无人机 class Drone { public: void fly() { std::cout << "无人机稳定悬停在10米高空!\n"; } void print() { std::cout << "当前电量:85%,信号强度:满格\n"; } }; int main() { AnyFlyerPrinter sparrow{Bird{}}; sparrow.fly(); sparrow.print(); AnyFlyerPrinter quadcopter{Drone{}}; quadcopter.fly(); quadcopter.print(); // 如果传入不符合要求的类型,编译期会直接触发静态断言报错 // AnyFlyerPrinter invalid{int{}}; // 取消注释会看到编译错误 return 0; }
额外小提示
- 静态断言的作用:在编译期就拦截不符合要求的类型,避免等到运行时才发现问题,这是C++17里替代concepts做类型约束的常用手段。
std::decay_t和完美转发:确保我们能正确处理左值、右值、const修饰的各种参数,避免不必要的拷贝操作。- 为什么不直接多继承两个Concept?其实也可以这么做,但直接定义一个包含双操作的统一抽象类,代码会更简洁,也更符合类型擦除的单一接口原则。
备注:内容来源于stack exchange,提问作者Mohammed Li
相关产品推荐
相关产品推荐

