Detectar la existencia de un miembro privado


Quiero escribir un rasgo de tipo para comprobar si algún tipo tiene un miembro member. Si member fuera público, hay muchas maneras de hacer esto (p. ej. void_t), el más conciso de los cuales es probablemente Yakk's can_apply (que eventualmente podría ser llamado 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

Pero si el miembro fuera privado , este rasgo falla, ya que el acceso en member está mal formado (no somos amigos) y no hay diferenciación entre mal formado debido a razones de acceso y mal formado debido a esto-cosa-no-existe razones:

class D {
    int member;
};

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

¿Hay alguna forma de escribir un comprobador de miembros en todos los controles de acceso?

Author: Community, 2015-09-14

2 answers

De hecho, hay una manera para los tipos de clase no-final no-unión:

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)){}>;

Demo. El truco es comprobar si la búsqueda en diferentes clases base producirá una ambigüedad. [clase.miembro.búsqueda]/2:

La búsqueda de nombres de miembros determina el significado de un nombre (id-expression) en un ámbito de clase (3.3.7). La búsqueda de nombres puede resultar en una ambigüedad , en en cuyo caso el programa está mal formado. []] Se lleva a cabo la búsqueda de nombres antes del control de acceso (3.4, Cláusula 11).

Tenga en cuenta que la búsqueda de GCCs está rota en la medida en que ignora los nombres que no son de tipo para la búsqueda en typename-specifier s.

 22
Author: Columbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-11 17:38:52

Puede crear otra clase MemberBase que tenga ese miembro, y luego subclase las dos clases (la clase a comprobar T y BaseMember) e intente acceder al miembro de la subclase. Si T también tiene un miembro member, entonces obtendrá un problema de ambigüedad.

Código:

#include <type_traits>

// Yakk's 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

Sin embargo, esto definitivamente huele como un truco sucio para mí.

 4
Author: Petr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-14 14:30:37