首页 > 解决方案 > 检测特定的 C++ 类型是否有成员,不包括继承的成员

问题描述

我想检测一个特定类型是否有一个成员:直接而不是作为继承的结果

目的是确定特定类型是否“具有特征”,例如序列化能力。自然地,为了扩展示例,即使父类型有,子类型也可能不具备序列化的能力。

当使用is_member_function_pointer或其他检测机制时,继承在起作用。请注意,即使 B 没有定义成员,输出也是“1”。

#include <type_traits>
#include <iostream>

struct A {
   void member() { }
};

struct B : A {
};

int main()
{
    std::cout << "B has member? "
              << std::is_member_function_pointer<decltype(&B::member)>::value
              << std::endl; 
}

我能够实现的最接近的是使用不可转换指针(B**没有隐式转换为A**)时,尽管使用起来有点尴尬。它还强加与类型匹配的附加参数并防止任何直接继承。

#include <type_traits>
#include <iostream>

struct A {
    // would match std::declval<B*> as B* -> A*,
    // hence forcing failure through B** -> A**.
    // void member(A*) { }
    void member(A**) { }
};

struct B : A {
    // succeeds compilation aka "found" if not commented
    // void member(B**) { }
};

int main()
{
    // This actually fails to compile, which is OKAY because it
    // WORKS when used with SFINAE during the actual detection.
    // This is just a simple example to run.
    //   error: invalid conversion from 'B**' to 'A**'
    std::cout << "B has member? "
              << std::is_member_function_pointer<
                decltype(std::declval<B>().member(std::declval<B**>()))
              >::value
              << std::endl; 
}

标签: c++inheritancec++14detectiontypetraits

解决方案


有一个巧妙的技巧可以帮助解决这个问题。

的类型&B::member实际上是void (A::*)(),而不是void (B::*)()(如果member是继承的)。

使用 SFINAE 检查是否&B::member存在具有正确的类型:

template <typename T, typename = void>
struct has_member : std::false_type {};

template <typename T> struct has_member
    <T, std::enable_if_t<std::is_same_v<void (T::*)(), decltype(&T::member)>>>
    : std::true_type
{};

这仅适用于一种特定的成员类型(member必须是void member())。将它概括为任何类型留给读者作为练习。


或者您可以花哨并使用由于void (B::*)()某种原因void (A::*)()在传递模板参数时不能隐式转换为特定的事实:

template <typename T, T>
struct detect_member_helper {};

template <typename T>
using detect_member = detect_member_helper<void (T::*)(), &T::member>;

template <typename T>
inline constexpr bool has_member = std::experimental::is_detected_v<detect_member, T>;

推荐阅读