Как указано в ответе AnT, это, по-видимому, является упущением стандарта и отсутствием правильного синтаксиса для выражения того, что вы хотите сделать. Мой коллега столкнулся с этой проблемой на днях и привел ваш вопрос и его ответ как доказательство того, что это не может быть сделано. Ну, мне нравится вызов, и да, это можно сделать ... но это не красиво.
Во-первых, важно понимать, что указатель на член в основном является смещением от указателя на структуру [1] . В языке есть оператор offsetof, который подозрительно похож на этот, и, что интересно, дает нам выразительность, необходимую для того, что мы хотим.
Проблема, с которой мы столкнулись, заключается в том, что C ++ запрещает указатели на члены-члены. Ну, почти ... у нас есть профсоюз, который замотал наши рукава для этого. Как я уже сказал, это не красиво!
Наконец, нам также нужно знать правильный тип указателя для приведения.
Итак, без лишних слов, вот код (протестирован на gcc и clang):
template <typename C, typename T, /*auto*/size_t P>
union MemberPointerImpl final {
template <typename U> struct Helper
{ using Type = U C::*; };
template <typename U> struct Helper<U&>
{ using Type = U C::*; };
using MemberPointer = typename Helper<T>::Type;
MemberPointer o;
size_t i = P; // we can't do "auto i" - argh!
static_assert(sizeof(i) == sizeof(o));
};
#define MEMBER_POINTER(C, M) \
((MemberPointerImpl<__typeof__(C), \
decltype(((__typeof__(C)*)nullptr)->M), \
__builtin_offsetof(__typeof__(C), M) \
>{ }).o)
Давайте сначала посмотрим на макрос MEMBER_POINTER
. Требуется два аргумента. Первая, C
, является структурой, которая будет основой для указателя на член. Заключение в __typeof__
не является строго необходимым, но позволяет передавать либо тип, либо переменную. Второй аргумент, M
, предоставляет выражение, члену которого мы хотим указатель.
Макрос MEMBER_POINTER
извлекает из этих аргументов две дополнительные части информации и передает их в качестве параметров в объединение шаблонов MemberPointerImpl
. Первая часть - это тип указанного члена. Это делается путем построения выражения с использованием нулевого указателя, на котором мы используем decltype
. Второй кусок - это смещение от базовой структуры к рассматриваемому члену.
Внутри MemberPointerImpl
нам нужно создать тип MemberPointer
, который будет тем, что возвращается макросом. Это делается с помощью вспомогательной структуры, которая удаляет ссылки, которые бесполезно возникают, если наш член является элементом массива, что также позволяет поддерживать это. Это также позволяет gcc и clang дать нам хороший полностью расширенный тип в диагностике, если мы присвоим возвращаемое значение переменной с несовпадающим типом.
Итак, чтобы использовать MEMBER_POINTER
, просто измените свой код с:
bool MyStruct::* toto = &MyStruct::inner.c;
до:
bool MyStruct::* toto = MEMBER_POINTER(MyStruct, inner.c);
[1] Хорошо, момент предостережения: это может быть не так для всех архитектур / компиляторов, поэтому переносчики, пишущие код, сейчас отводят взгляд!