如何用模板机制调用签名相同但名称不同的类方法(不修改原类)
适配不同方法名的模板类实现方案
首先先把问题里的原始代码贴出来,方便对照:
struct C { int d1; int d2; }; struct A { void write(C data) { } }; struct B { void use(C data) { } }; // 需要实现的模板类D template <class T> struct D { T t; D(T myT) : t(myT) { } // 这里建议用初始化列表,比赋值更规范高效 void myFunct(C data) { // 这里要调用t的对应方法,但A是write,B是use } };
没问题,这个需求完全可以通过C++模板本身的特性搞定,不用修改原有的A、B类,也不需要创建子类来统一方法名。我给你几个实用的实现方案,你可以根据场景选择:
方案一:模板特化
针对A和B分别特化D的myFunct方法,编译器会根据T的类型自动匹配对应的特化版本:
// 通用模板(可以留空,或者给其他类型加提示) template <class T> struct D { T t; D(T myT) : t(myT) { } void myFunct(C data) { // 如果传入未特化的类型,静态断言会报错提示 static_assert(false, "D is not specialized for this type"); } }; // 针对A类的特化实现 template <> void D<A>::myFunct(C data) { t.write(data); } // 针对B类的特化实现 template <> void D<B>::myFunct(C data) { t.use(data); }
这种方式特别直接,适合需要适配的类型不多的场景——你只需要针对每个目标类型,单独写出myFunct的特化版本就行。
方案二:用额外模板参数传递方法指针
如果想让D更通用,支持任意带有类似方法的类,可以把方法作为模板参数传进去,这样不用为每个类写特化:
template <class T, void (T::*Method)(C)> struct D { T t; D(T myT) : t(myT) { } void myFunct(C data) { (t.*Method)(data); // 调用传入的成员方法指针 } }; // 使用示例: A a; D<A, &A::write> d_a(a); d_a.myFunct(C{}); B b; D<B, &B::use> d_b(b); d_b.myFunct(C{});
这个方案灵活性拉满,只要是类里有接收C类型参数的void方法,都可以用D来适配。缺点是使用时需要明确指定方法指针,但换来了极强的通用性。
方案三:标签分发(自动检测方法)
利用C++的SFINAE特性和constexpr if,让编译器自动判断类T有哪个方法,然后调用对应的版本,完全不用手动指定:
#include <type_traits> // 需要包含这个头文件 namespace detail { // 定义两个标签,用来区分不同的方法调用逻辑 struct WriteTag {}; struct UseTag {}; // 检测T是否有write方法(SFINAE技巧) template <class T> auto has_write_method(int) -> decltype(std::declval<T>().write(std::declval<C>()), std::true_type{}); template <class T> std::false_type has_write_method(...); // 检测T是否有use方法 template <class T> auto has_use_method(int) -> decltype(std::declval<T>().use(std::declval<C>()), std::true_type{}); template <class T> std::false_type has_use_method(...); // 根据标签调用对应的方法 template <class T> void call_method(T& t, C data, WriteTag) { t.write(data); } template <class T> void call_method(T& t, C data, UseTag) { t.use(data); } } // 主模板类 template <class T> struct D { T t; D(T myT) : t(myT) { } void myFunct(C data) { if constexpr (decltype(detail::has_write_method<T>(0))::value) { detail::call_method(t, data, detail::WriteTag{}); } else if constexpr (decltype(detail::has_use_method<T>(0))::value) { detail::call_method(t, data, detail::UseTag{}); } else { static_assert(false, "T has neither write nor use method"); } } };
这个方案最智能,使用的时候完全不用关心方法名,编译器会自动检测并调用正确的方法。需要注意的是,这个方案需要C17及以上版本的constexpr if支持;如果是更早的C版本,可以用SFINAE来重载myFunct方法实现类似效果。
内容的提问来源于stack exchange,提问作者mans
相关产品推荐
相关产品推荐

