Определить, есть ли у указанного типа c C ++ член, ИСКЛЮЧАЯ унаследованные члены - PullRequest
3 голосов
/ 12 февраля 2020

Я хотел бы определить, есть ли у определенного типа член: напрямую и не в результате наследования .

Цель состоит в том, чтобы определить, если специфицирует c тип «имеет черту», ​​такую ​​как возможность сериализации. Естественно, и для расширения примера, подтип может не иметь возможности сериализации, даже если родительский тип имеет.

  • Есть ли (и если да) «стандартное» решение этого запроса ?
  • Есть ли недостаток (исключая налагаемые им ограничения) в подходе неконвертируемого указателя, представленном внизу?

При использовании is_member_function_pointer или других механизмов обнаружения наследование в игре. Обратите внимание, что вывод «1», хотя B не определяет член.

#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; 
}

Ответы [ 3 ]

3 голосов
/ 12 февраля 2020

Есть полезный трюк, который может помочь с этим.

Тип &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
{};

Это работает только для одного указанного c типа элемента (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>;
2 голосов
/ 12 февраля 2020

Это может работать для вас:

#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

1 голос
/ 12 февраля 2020

Используя тот факт, что &B::member равен void (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
{};

Демо

...