Подтвердить что ты не робот

Обнаружение существования частного участника

Я хочу написать черту типа, чтобы проверить, имеет ли какой-то тип член member. Если member были общедоступными, существует множество способов сделать это (например, void_t), наиболее кратким из которых является, вероятно, Yakk can_apply (который в итоге можно было бы назвать std::is_detected):

struct C {
    int member;
};

template <typename T>
using member_type = decltype(&T::member);

template <typename T>
using has_member = can_apply<member_type, T>;

static_assert(has_member<C>{}, "!"); // OK

Но если член был закрыт, этот признак терпит неудачу, поскольку доступ на member плохо сформирован (мы не друзья), и нет никакой дифференциации между плохо сформированными из-за причин доступа и плохо сформированными из-за причины этого не существуют:

class D {
    int member;
};

static_assert(has_member<D>{}, "!"); // error

Есть ли способ написать такую ​​проверку элемента во всех элементах управления доступом?

4b9b3361

Ответ 1

Существует действительно способ для не конечных типов неединичных классов:

namespace detail {
    struct P {typedef int member;};
    template <typename U>
    struct test_for_member : U, P
    {
        template <typename T=test_for_member, typename = typename T::member>
        static std::false_type test(int);
        static std::true_type test(float);
    };
}
template <typename T>
using test_for_member =
  std::integral_constant<bool, decltype(detail::test_for_member<T>::test(0)){}>;

Демо. Трюк состоит в том, чтобы проверить, приведет ли поиск к различным базовым классам двусмысленность. [Class.member.lookup]/2:

Поиск имени пользователя определяет значение имени (id-expression) в классе (3.3.7). Поиск имени может привести к двусмысленности, в в этом случае программа плохо сформирована. [...] Поиск имени происходит до контроля доступа (3.4, раздел 11).

Обратите внимание, что поиск GCC нарушен, поскольку он игнорирует имена не-типа для поиска в typename-спецификаторах.

Ответ 2

Вы можете создать еще один класс MemberBase, который имеет этот член, а затем подклассифицировать два класса (класс для проверки T и BaseMember) и попытаться получить доступ к члену подкласса. Если T также имеет член member, тогда вы столкнетесь с проблемой двусмысленности.

код:

#include <type_traits>

// Yakk can_apply

template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;

template<class...>struct types{using type=types;};
namespace details {
  template<template<class...>class Z, class types, class=void>
  struct can_apply : std::false_type {};
  template<template<class...>class Z, class...Ts>
  struct can_apply< Z, types<Ts...>, void_t< Z<Ts...> > >:
    std::true_type
  {};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,types<Ts...>>;

// Main code

class MemberBase {
    public:
        int member;
};

template<class ToCheck>
class MemberCheck: public ToCheck, public MemberBase {
};

template <typename T>
using member_type = decltype(&T::member);

template <typename T>
using hasnot_member = can_apply<member_type, MemberCheck<T>>;

template <typename T> 
using static_not = std::integral_constant<bool, !T::value>;

template <typename T>
using has_member = static_not<hasnot_member<T>>;

// Tests

class A {
    int member;
};

class Ap {
    public:
    int member;
};

class B {
    float member;
};

class C {
    int member();
};

class D {
};

static_assert(has_member<A>{}, "!"); // ok
static_assert(has_member<Ap>{}, "!"); // ok
static_assert(has_member<B>{}, "!"); // ok
static_assert(has_member<C>{}, "!"); // ok
static_assert(has_member<D>{}, "!"); // fail

Однако это определенно пахнет грязным хаком для меня.