【问题标题】:Detect if a specific C++ type has a member, EXCLUDING inherited members检测特定的 C++ 类型是否有成员,不包括继承的成员
【发布时间】:2020-05-27 10:20:39
【问题描述】:

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

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

  • 是否有(如果有)针对此请求的“标准”解决方案?
  • 底部显示的不可转换指针方法是否存在缺陷(不包括其施加的限制)?

当使用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; 
}

【问题讨论】:

  • 您可以要求传递给函数的东西专门为您的库创建的某些类型特征,而不是尝试检测成员的存在,然后您可以检查类型特征的结果。
  • @NutCracker 不,它不会 - 除非细节被埋没。该答案/问题不涉及 inheritance,而这正是问题所在。
  • @NathanOliver 对于“不显眼的表单”也是如此:) 我希望对“显眼/内联”表单也有类似的效果。
  • @BasileStarynkevitch 所以现在我只需要为 Clang、GCC、IIC、NVCC 等编写、安装和维护插件 ;-)

标签: c++ inheritance c++14 detection typetraits


【解决方案1】:

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

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

使用 SFINAE 检查 &amp;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>;

【讨论】:

  • 它真的不起作用。检查this示例
  • @NutCracker 那是因为它需要 member 的不同签名:godbolt.org/z/MG5C_S
  • @HolyBlackCat 哇,那.. 有效!昨天我不知道如何在is_same_v 工作。注意到“&B::member 的类型实际上是 void (A::*)()”也很方便。一些小垫片和在 Clang 3.9.1 中的工作(是的,旧的,它是我们的 .. 基线 .. 目标之一)。
【解决方案2】:

这可能对你有用:

#include <type_traits>
#include <iostream>

struct A {
    void member(A**)
    {}
};

struct B : A
{};

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

template <typename T>
struct hasMember<T, std::void_t<decltype(std::declval<T*>()->member())>>
    : std::is_same<decltype(std::declval<T*>()->member()), void>
{};

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

输出将是B has member ? 0

看看live

【讨论】:

  • [专业提示]((T*)nullptr)可以替换为std::declval&lt;T*&gt;()
  • 谢谢。这就是原始代码中的代码的有效使用方式(例如,SFINAE/检测器被淘汰)。但是,它仍然依赖于不可转换的指针/非覆盖方法来运行。它在这里是可行的..只是希望它可以在不需要这样的情况下引出。
【解决方案3】:

使用&amp;B::membervoid (A::*)() 的事实,您可能会这样做

template <typename C, typename Sig>
struct classMember : std::false_type{};

template <typename C, typename Ret, typename ... Ts>
struct classMember<C, Ret (C::*)(Ts...)> : std::true_type{};
// and other specialization for cv and ref and c ellipsis

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

template <typename T>
struct hasMember<T, std::enable_if_t<classMember<T, decltype(&T::member)>::value>> : std::true_type
{};

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 2019-03-23
    • 2015-08-09
    • 2020-10-21
    • 2020-09-01
    • 1970-01-01
    相关资源
    最近更新 更多