c++ - subclases - Detectar existencia de miembro privado
que es un objeto en c++ (2)
Quiero escribir un rasgo de tipo para verificar si algún tipo tiene un miembro member
. Si el member
fue público , hay varias maneras de hacerlo (por ejemplo, void_t
), la más concisa de las cuales probablemente sea Yakk''s can_apply
(que eventualmente podría llamarse 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 era privado , este rasgo falla, ya que el acceso al member
está mal formado (no somos amigos) y no hay diferencia entre mal formado debido a razones de acceso y mal formado debido a esto-cosa-doesn ''t-existen razones:
class D {
int member;
};
static_assert(has_member<D>{}, "!"); // error
¿Hay alguna forma de escribir dicho verificador de miembro en todos los controles de acceso?
De hecho, hay una manera para tipos de clases no unidas finales:
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 verificar si la búsqueda en diferentes clases base generará una ambigüedad. [class.member.lookup] / 2:
La búsqueda de nombre de miembro determina el significado de un nombre ( expresión-id ) en un ámbito de clase (3.3.7). La búsqueda de nombres puede dar lugar a una ambigüedad , en cuyo caso el programa está mal formado. [...] La búsqueda de nombres tiene lugar antes del control de acceso (3.4, Cláusula 11).
Tenga en cuenta que la búsqueda de GCC está rota en la medida en que ignora los nombres que no son de tipo para la búsqueda en typename-specifier s.
Puede crear otra clase MemberBase
que sí tenga ese miembro, y luego subclasificar las dos clases (la clase para verificar T
y BaseMember
) e intentar acceder al miembro de la subclase. Si T
también tiene un member
miembro, 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 sucio truco para mí.