2016-04-19 1 views
8

Я хотел бы проверить, является ли переменная-член класса статичной или нет. Использование std :: is_member_pointer работает отлично для всех типов, за исключением ссылочных элементов.Характеристика типа: проверьте, является ли ссылочная переменная элемента статической или нет.

#include <type_traits> 

struct A { 
    int foo; 
}; 

struct B : A {}; 

struct C { 
    static int foo; 
}; 

struct D : C { 
}; 

struct E { 
    int &foo; 
}; 

struct F { 
    static int &foo; 
}; 

static_assert(std::is_member_pointer<decltype(&A::foo)>::value, "No"); 
static_assert(std::is_member_pointer<decltype(&B::foo)>::value, "No"); 
static_assert(!std::is_member_pointer<decltype(&C::foo)>::value, "No"); 
static_assert(!std::is_member_pointer<decltype(&D::foo)>::value, "No"); 

// Fail to compile: 
static_assert(std::is_member_pointer<decltype(&E::foo)>::value, "No"); 

static_assert(!std::is_member_pointer<decltype(&F::foo)>::value, "No"); 

Live example.

Я понимаю ошибку, что указатель не может указывать на опорный элемент. Но как его избежать и по-прежнему различать, является ли это статической или нестатической переменной? Есть идеи по этому поводу?

+1

Связанный: http://stackoverflow.com/questions/8336274/pointer-to-member-that-is-a-reference-illegal – PiotrNycz

ответ

2

Вы можете добавить запасной вариант в случае, если &E::foo терпит неудачу с использованием SFINAE (и еще один в случае E::foo не существует вообще):

template <typename T> 
std::is_member_pointer<decltype(&T::foo)> is_member_foo(int); 

template <typename T> 
decltype(T::foo, std::true_type{}) is_member_foo(long); 

template <typename T> 
std::false_type is_member_foo(...); 

template <typename T> 
using IsMemberFoo = decltype(is_member_foo<T>(0)); 

static_assert(IsMemberFoo<A>{}, "No"); 
static_assert(IsMemberFoo<B>{}, "No"); 
static_assert(!IsMemberFoo<C>{}, "No"); 
static_assert(!IsMemberFoo<D>{}, "No"); 
static_assert(IsMemberFoo<E>{}, "No"); 
static_assert(!IsMemberFoo<F>{}, "No"); 
static_assert(!IsMemberFoo<G>{}, "No"); // struct G { }; 

Что делает этот код:

  • Если &T::foo является valid, он проверяет, является ли элемент статическим или не использует std::is_member_pointer (ваша версия).
  • Если &T::foo не является действительным, он возвращается ко второй перегрузке (здесь вы уверены, что foo не является статичным, или первая перегрузка была бы выбрана):
    • Если T::foo правомочно (член существует), он возвращает std::true_type.
    • Otherwize он возвращается к последней перегрузке и возвращает std::false_type.

Также обратите внимание (благодаря @iammilind), что для private члена, T::foo является не действует, поэтому третья перегрузка будет выбран.

Рабочий пример на ideone: http://ideone.com/FILHbK

сторона отмечает (расширенное объяснение):

  • Когда &T::foo действительно, две первые перегрузки справедливы, но первый один выбирается так int является точным в то время как long нет.
  • decltype(T::foo, std::true_type{}): T::foo только здесь, чтобы «позволить SFINAE» вернуться к третьей перегрузке, если T::foo недействителен, но полученный результат std::true_type благодаря оператору запятой.

Если вы хотите, вы также можете создать версию родовое (http://ideone.com/lzH2FB):

#define IsMember(MEM) \ 
template <typename T> \ 
std::is_member_pointer<decltype(&T::MEM)> is_member_##MEM(int); \ 
template<typename T> \ 
decltype(T::MEM, std::true_type{}) is_member_##MEM(long); \ 
template <typename T> \ 
std::false_type is_member_##MEM(...); \ 
template <typename T> \ 
using IsMember_##MEM = decltype(is_member_##MEM<T>(0)); 

// Instanciate IsMember_foo 
IsMember(foo); 

// Use it: 
static_assert(IsMember_foo<A>{}, "No"); 

Также смотрите эти два ответа, если вы хотите, чтобы инкапсулировать все в классе (без is_member_ функций) :

+0

Ваш ответ кажется правильным для меня. Упрощенный. Можете ли вы внести некоторые изменения в код примера? 1. 'C' повторяется дважды, но' D' не существует. 2. Замените «typename C» на 'typename T', поскольку это создает путаницу для читателей (уже есть класс C). И в примере кода, и в вашем ответе. BTW, ваше понимание SFINAE неплохое, и вместо создания автономных кодов, как описано выше, вы должны создать библиотеку, такую ​​как код с использованием макроса. Также отметьте, что это может не работать для 'private' переменных. – iammilind

+0

@iammilind Спасибо, я обновил ответ и код ideone. – Holt

+0

Ваш новый способ создания универсального участника хорош. Но представьте себе, что это приведет к созданию большого количества 'is_member _ ## MEM' из-за его исключительности. Следовательно, вы должны думать о том, как вы можете инкапсулировать в классе и продолжать использовать один и тот же. Технически нет ничего плохого, даже если вы создадите так много таких шаблонов. Но просто не нужно этого делать. Дополнительным преимуществом 'class' является то, что вы можете иметь значение« bool », которое также может использоваться для печати и проверки. – iammilind

Смежные вопросы