如何以通用方式实现适配多函数的C++ getterFunction?
我需要为多个函数(function1()、function2()等)实现相关逻辑,目前为每个函数分别定义了对应的concept和getterFunction重载:
template <typename T> concept iterator_1 = requires(T const& t) { t.function1(); };
template <iterator_1 R> static float getterFunction(R const& r) { return r.function1(); }
template <typename T> concept iterator_2 = requires(T const& t) { t.function2(); };
template <iterator_2 R> static float getterFunction(R const& r) { return r.function2(); }
我希望用一个通用的getterFunction,通过条件判断选择调用function1()、function2()等函数,示例如下:
template <typename A> float getterFunction(A const& a) { auto i = -1; if (something==0) { i = a.function1(); } else if (something==1) { i = a.function2(); } else if { ... } return i; }
并按如下方式使用该通用函数:
template <typename B> void DoSomething(typename B::iterator const& b) { float value = getterFunction(b); ... }
请问是否存在更通用的实现方式,无需为每个函数编写单独的concept?
当然有,不需要单独定义每个concept,以下是几种更通用的实现方式:
方式一:C++20 constexpr if 结合 requires 表达式
利用C++20的constexpr if和就地的requires表达式,直接在函数内部判断类型是否支持某个成员函数,不需要额外定义concept:
template <typename A> float getterFunction(A const& a, int something) { if constexpr (requires { a.function1(); }) { if (something == 0) { return a.function1(); } } if constexpr (requires { a.function2(); }) { if (something == 1) { return a.function2(); } } // 处理其他情况或返回默认值 return -1.0f; }
这里的requires { a.function1(); }会在编译期检查类型A是否拥有function1()成员函数,只有满足条件的分支才会被编译。如果something是编译期常量,还可以把条件合并到constexpr if中,进一步优化编译结果。
方式二:SFINAE(适用于C++11及以上版本)
如果需要兼容C++11/14,可以用SFINAE(替换失败不是错误)机制,通过重载和返回值推导实现:
#include <type_traits> // 匹配拥有function1的类型 template <typename A> auto getterFunction(A const& a, int something) -> decltype(a.function1()) { if (something == 0) return a.function1(); // 若something不为0,转向内部实现逻辑 return getterFunction_impl(a, something, std::false_type{}); } // 匹配拥有function2的类型 template <typename A> auto getterFunction(A const& a, int something) -> decltype(a.function2()) { if (something == 1) return a.function2(); return getterFunction_impl(a, something, std::false_type{}); } // 默认 fallback 逻辑 template <typename A> float getterFunction_impl(A const&, int, std::false_type) { return -1.0f; }
这种方式通过返回值类型的推导触发SFINAE,只有当类型支持对应成员函数时,对应的重载才会被编译器选中。
方式三:Trait检测 + 条件判断
先封装检测类型是否拥有特定成员函数的trait,再在函数内部根据trait结果和业务条件选择调用逻辑:
#include <type_traits> // 检测类型是否拥有const版本的function1 template <typename T> struct has_function1 { template <typename U> static auto test(int) -> decltype(std::declval<U const&>().function1(), std::true_type{}); template <typename U> static std::false_type test(...); static constexpr bool value = decltype(test<T>(0))::value; }; // 检测类型是否拥有const版本的function2 template <typename T> struct has_function2 { template <typename U> static auto test(int) -> decltype(std::declval<U const&>().function2(), std::true_type{}); template <typename U> static std::false_type test(...); static constexpr bool value = decltype(test<T>(0))::value; }; template <typename A> float getterFunction(A const& a, int something) { if (something == 0 && has_function1<A>::value) { return a.function1(); } else if (something == 1 && has_function2<A>::value) { return a.function2(); } // 默认返回值 return -1.0f; }
这种方式把类型检测逻辑抽离成可复用的trait,函数内部的逻辑更清晰。
注意事项
你示例中的something需要作为参数传入函数(除非它是编译期常量),否则编译器无法在编译期确定分支的有效性,可能会触发编译错误。如果something是编译期常量,可以将其作为模板参数传入,进一步优化分支的编译结果。
内容的提问来源于stack exchange,提问作者Gyulaxxx

