如何在C++中将类定义复用为不同类型?
复用C++类定义同时让编译器视为不同类型的解决方案
当用using Bar = Foo;或typedef Foo Bar;复用类定义时,编译器会把两者视作同一类型,导致重载函数触发重定义错误,比如:
class Foo {}; using Bar = Foo; void fun(const Foo&) { /* ... */ } void fun(const Bar&) { /* ... */ } // 编译报错:函数重定义
补充场景里,MyFoo::type和MyBar::type指向同一类型,直接重载构造函数也会报重定义:
struct SomeRelated {}; struct MyFoo { using type = SomeRelated; }; struct MyBar { using type = SomeRelated; }; struct MyClass { MyClass(const MyFoo::type&) { /* ... */ } // 编译报错 MyClass(const MyBar::type&) { /* ... */ } };
下面是几种可行的解决办法:
1. 空标记类包装(最常用最干净)
核心思路是用空标记类区分不同的包装类型,同时继承原类以复用所有成员。
针对基础场景:
class Foo {}; // 定义两个空标记类,用于区分类型 struct FooTag {}; struct BarTag {}; // 带标记的模板包装类,继承原类Foo template<typename Tag> struct WrappedFoo : public Foo {}; // 定义新类型 using OriginalFoo = WrappedFoo<FooTag>; using Bar = WrappedFoo<BarTag>; // 现在重载可以正常编译 void fun(const OriginalFoo&) { cout << "It is Foo" << endl; } void fun(const Bar&) { cout << "It is Bar" << endl; }
针对补充场景:
给关联类型套一层带标记的包装,让编译器识别为不同类型:
struct SomeRelated {}; // 通用带标记的包装模板,支持隐式转成原类型方便使用 template<typename Tag, typename T> struct TaggedType { T value; operator const T&() const { return value; } operator T&() { return value; } }; // 定义标记 struct MyFooTag {}; struct MyBarTag {}; // 修改关联类型为带标记的版本 struct MyFoo { using type = TaggedType<MyFooTag, SomeRelated>; }; struct MyBar { using type = TaggedType<MyBarTag, SomeRelated>; }; struct MyClass { MyClass(const MyFoo::type&) { cout << "ctor from MyFoo" << endl; } MyClass(const MyBar::type&) { cout << "ctor from MyBar" << endl; } }; // 使用示例 MyFoo::type foo_val; MyBar::type bar_val; MyClass c1(foo_val); // 输出 "ctor from MyFoo" MyClass c2(bar_val); // 输出 "ctor from MyBar"
2. 强类型包装(C++20+)
如果用C++20及以上,可以自己实现或用第三方库的强类型包装,本质和标记方法类似,但封装更完善,还能避免隐式转换带来的意外。
简易实现示例:
template<typename T, typename Tag> struct StrongType { // 显式构造,避免隐式转换 explicit StrongType(T val) : value(std::move(val)) {} T& get() { return value; } const T& get() const { return value; } private: T value; }; // 基础场景使用 class Foo {}; using Bar = StrongType<Foo, struct BarTag>; void fun(const Foo&) { cout << "It is Foo" << endl; } void fun(const Bar&) { cout << "It is Bar" << endl; }
3. 继承+空成员(不推荐)
让新类继承原类,再添加一个空成员变量,编译器会认为是不同类型,但可能因空基类优化导致内存占用不变,不过代码不够优雅,不如标记方法清晰:
class Foo {}; class Bar : public Foo { char dummy; // 空成员用于区分类型,实际可能被优化掉 };
内容的提问来源于stack exchange,提问作者hczstev
相关产品推荐
相关产品推荐

