如何判断 C++ 类里是否有某个函数?
🔀

如何判断 C++ 类里是否有某个函数?

Published
March 10, 2025
Author
WuZheng

背景

什么时候想要判断类里是否有某个函数?似乎使用场景不多。这里举个例子:
日志打印,将任意类型的对象转换为字符串打印出来。可以检查该对象是否有 ToString 方法,有的话则调用该方法获取返回值进行打印。
那么这里就以判断类是否有 ToString 函数为例,先从简单的开始:

判断类是否有 ToString() 方法,不指定参数和返回值

#include <string> #include <iostream> #include <type_traits> // Helper to detect if a type has a ToString method with no parameters template<typename T> class HasToStringFunction { private: // This overload will be chosen if T has a ToString method with no parameters template<typename U> static auto Check() -> decltype(std::declval<U>().ToString(), std::true_type()); // This overload will be chosen if T does not have a ToString method with no parameters template<typename U> static std::false_type Check(...); public: // The value will be true if the first overload is chosen, false otherwise static constexpr bool value = std::is_same<decltype(Check<T>()), std::true_type>::value; };

代码解释

  • typename:告诉编译器,后面的名字是类型,而不是变量,详细的解释参考这篇文章
  • decltype:编译器类型推导,获取变量或者表达式的类型
  • std::true_type, std::false_type:在数值计算中,有 true/false 值表示真假,在类型计算中 true/false 都是 bool,需要在类型的世界中有表示真假的类型,就是 true_type 和 false_type 了
  • 在编译期,代码会判断类型 T 能够匹配到哪个 Check,如果 T 类型有 ToString 方法,则匹配到第一个 Check 函数,返回 std::true_type,接着 value 会被设置为 true。

测试

// Online editor: https://godbolt.org/z/3vE9WqWaY // 函数签名:无参数,无返回值 class ToStringNoArgsReturnVoid { public: void ToString() {}; }; // 函数签名:有参数,无返回值,可与上面的 ToStringNoArgsReturnVoid 对比验收参数类型检查的结果 class ToStringIntArgsReturnVoid { public: void ToString(int i) {}; }; // 函数签名:无参数,有返回值,可与上面的 ToStringNoArgsReturnVoid 对比验收返回值类型的检查结果 class ToStringNoArgsReturnInt { public: int ToString() { return 1; }; }; // 无 ToString 方法 class NoToString { }; // 访问权限为 private 的 ToString 方法,可对比得出访问权限是否影响函数的存在性检测 class PrivateToStringNoArgsReturnVoid { private: void ToString() {}; }; int main() { if (HasToStringFunction<ToStringNoArgsReturnVoid>::value) { std::cout << "ToStringNoArgsReturnVoid class has ToString()" << std::endl; } if (!HasToStringFunction<ToStringIntArgsReturnVoid>::value) { std::cout << "ToStringIntArgsReturnVoid class has no ToString()" << std::endl; } if (HasToStringFunction<ToStringNoArgsReturnInt>::value) { std::cout << "ToStringNoArgsReturnInt class has ToString() " << std::endl; } if (!HasToStringFunction<NoToString>::value) { std::cout << "NoToString class has no ToString() " << std::endl; } if (!HasToStringFunction<PrivateToStringNoArgsReturnVoid>::value) { std::cout << "PrivateToStringNoArgsReturnVoid class has no ToString()" << std::endl; } } /** output: ToStringNoArgsReturnVoid class has ToString() ToStringIntArgsReturnVoid class has no ToString() ToStringNoArgsReturnInt class has ToString() NoToString class has no ToString() PrivateToStringNoArgsReturnVoid class has no ToString() **/
 

判断类是否有 ToString() 方法,可指定参数,不指定返回值

// Helper to detect if a type has a ToString method with any parameters template<typename T, typename... Args> class HasToString { private: // This overload will be chosen if T has a ToString method with the given parameters template<typename U> static auto Check() -> decltype(std::declval<U>().ToString(std::declval<Args>()...), std::true_type()); // This overload will be chosen if T does not have a ToString method with the given parameters template<typename> static std::false_type Check(...); public: // The value will be true if the first overload is chosen, false otherwise static constexpr bool value = std::is_same<decltype(Check<T>()), std::true_type>::value; };

代码解释

这里只需要了解(记住)传入可变参数的写法。模板参数会传递到 ToString 中参与匹配,达到匹配函数参数的目的。

测试

新增对带参数的 ToString 方法的判断,执行结果符合预期:
if (HasToString<ToStringIntArgsReturnVoid, int>::value) { std::cout << "ToStringIntArgsReturnVoid has ToString method with one int argument" << '\n'; } else { std::cout << "ToStringIntArgsReturnVoid does not have ToString method" << '\n'; } if (HasToString<ToStringIntArgsReturnVoid>::value) { std::cout << "ToStringIntArgsReturnVoid has ToString method" << '\n'; } else { std::cout << "ToStringIntArgsReturnVoid does not have ToString method without args" << '\n'; } /* output: ToStringIntArgsReturnVoid has ToString method with one int argument ToStringIntArgsReturnVoid does not have ToString method without args */

判断类是否有 ToString() 方法,可指定参数和返回值

template<typename T, typename RetType, typename... Args> class HasToStringWithReturnType { template<typename U> static auto Check() -> std::enable_if_t<std::is_same<RetType, decltype(std::declval<U>().ToString(std::declval<Args>()...))>::value, std::true_type>; template<typename U> static std::false_type Check(...); public: static constexpr bool value = std::is_same<decltype(Check<T>()), std::true_type>::value; };

代码解释

  • std::enable_if_t:如果 std::is_same<RetType, decltype(std::declval<U>().ToString(std::declval<Args>()...))>::value 是 true,则返回 std::true_type 类型的返回值,否则返回 void 类型的返回值。
  • 在编译期,能匹配到参数和返回值都是模板中指定的,则会命中第一个 Check,返回一个 std::true_type 的值,在 value 的赋值过程中通过 is_same 判断,设置为 true

测试

新增对有返回值的 ToString 方法的判断,执行结果符合预期:
if (HasToStringWithReturnType<ToStringNoArgsReturnInt, void>::value) { std::cout << "ToStringNoArgsReturnInt have ToString method return void" << '\n'; } else { std::cout << "ToStringNoArgsReturnInt does not have ToString method return void" << '\n'; } if (HasToStringWithReturnType<ToStringNoArgsReturnInt, int>::value) { std::cout << "ToStringNoArgsReturnInt have ToString method return int" << '\n'; } else { std::cout << "ToStringNoArgsReturnInt does not have ToString method return int" << '\n'; } /* output: ToStringNoArgsReturnInt does not have ToString method return void ToStringNoArgsReturnInt have ToString method return int */

参考