在python中,可以使用hasattr判断类型是否具有某个成员。
在C++中,有的时候我们要写一个模板函数,需要对模板进行一定的限制时。这些限制可能为“该模板函数仅用于拥有某个成员的类型”。在标准<type_traits>中,规定了一些列如is_copy_assignable等模板常量,用于判断是否拥有拷贝构造函数等成员,但对于其他自定义的成员,我们要如何判断呢?
在C++中有两种方式可以检查类型是否具有某个成员:
1、虚表运行期,通过多态和类似于COM组件地方式进行检查。这种方式可以在运行期间检查接口是否具有某个函数,通过SearchInterface这类型地函数进行接口查找,再通过AddRef等函数返回对应接口地指针。这种方式通过动态多态地方式判断某个已经存在地C++对象,是否具有某个接口函数地实现。但该种方式是通过从逻辑层面的判断,并通过dynamic_cast进行转换而进行hasattr的判断。但并不能直接判断某一个类是否具有某个成员函数。
2、元编程std:: SFINAE + void_t判断,在元编程中,类型分为有效类型和无效类型。void类型在普通的C++中是一个不完整类型,不能直接声明void对象,因为编译器不知道void类型的大小。但对于函数返回值来说,void是一个有效的类型。对于C++模板元编程来说,void是一个有效的类型。可以通过SFINAE+void_t来判断一个成员函数或者成员的类型是否有效,从而实现hasattr类似的效果。
SFINAE: Substitution Failure Is Not An Error 有兴趣的话可以上网查。大概意思就是,在编译期,模板匹配的时候,编译器会一直匹配,直到匹配到一个最合适的特化或者偏特化,如果中间出现匹配失败的话,也不会停。
以下我们用SFINAE+std::void_t 的方法来实现一个模板,用于判断类型是否具有my_test_func()成员,并支持const/non-const,左值/右值的判断。
template<typename T,typename = void>
constexpr bool has_my_test_func = false;
template<typename T>
constexpr bool has_my_test_func<T,std::void_t<decltype(
std::declval<std::conditional_t<
std::is_rvalue_reference_v<T>,
std::remove_reference_t<T>&&,
std::remove_reference_t<T>&>>().my_test_func()
)>> = true;
上半部分为has_my_test_func的声明,下半部分为一个偏特化。当我们使用模板has_my_test_func<T>时,编译器会先去匹配下面的偏特化,如果能够成功通过void_t的有效类型检测,则应用到true值,若无法通过void_t的检测,则匹配到楼上的通用模板,值为false。
下面为测试代码:
class A{};
class B{private: void my_test_func();};
class C{public: void my_test_func();};
class D{public: int my_test_func();};
class E{public: void my_test_func()&;};
class F{public: void my_test_func()&&;};
class G{public: void my_test_func()const;};
class H{public: void my_test_func()const&&;};
printf("%d\n",has_my_test_func<A>); // 0
printf("%d\n",has_my_test_func<B>); // 0
printf("%d\n",has_my_test_func<C>); // 1
printf("%d\n",has_my_test_func<D>); // 1
printf("%d\n",has_my_test_func<E>); // 1
printf("%d\n",has_my_test_func<E&&>); // 0
printf("%d\n",has_my_test_func<F>); // 0
printf("%d\n",has_my_test_func<F&&>); // 1
printf("%d\n",has_my_test_func<G>); // 1
printf("%d\n",has_my_test_func<const G>); // 1
printf("%d\n",has_my_test_func<H>); // 0
printf("%d\n",has_my_test_func<const H&>); // 0
printf("%d\n",has_my_test_func<const H&&>); // 1