, вероятно, нет. Он поймет, что первое слово после определения - это имя идентификатора, а остальное - это «тело» этого.
Да, это запрещено. Вы не первый, кто придумал эту совершенно логичную идею. По-моему, это один из очевидных «ошибок» / «упущений» в спецификации указателей на членов на C ++, но, по-видимому, комитет не заинтересован в дальнейшем развитии спецификации указателей на членов ( в случае с большинством «низкоуровневых» языковых функций).
Обратите внимание, что все необходимое для реализации функции уже существует на этом языке. Указатель на элемент a-data-of-a-member ничем не отличается от указателя на непосредственный элемент данных. Единственное, чего не хватает, это синтаксис для инициализации такого указателя. Однако комитет, по-видимому, не заинтересован в введении такого синтаксиса.
С чистой формальной логической точки зрения это должно было быть разрешено в C ++
struct Inner {
int i;
int j[10];
};
struct Outer {
int i;
int j[10];
Inner inner;
};
Outer o;
int Outer::*p;
p = &Outer::i; // OK
o.*p = 0; // sets `o.i` to 0
p = &Outer::inner.i; // ERROR, but should have been supported
o.*p = 0; // sets `o.inner.i` to 0
p = &Outer::j[0]; // ERROR, but should have been supported
o.*p = 0; // sets `o.j[0]` to 0
// This could have been used to implement something akin to "array type decay"
// for member pointers
p = &Outer::j[3]; // ERROR, but should have been supported
o.*p = 0; // sets `o.j[3]` to 0
p = &Outer::inner.j[5]; // ERROR, but should have been supported
o.*p = 0; // sets `o.inner.j[5]` to 0
. Типичная реализация элемента-указателя-данных - это не что иное, как просто байт-смещение члена от начала окружающего объекта. Поскольку все члены (непосредственные члены и члены членов) в конечном итоге последовательно выкладываются в памяти, члены членов также могут быть идентифицированы с помощью определенного значения смещения. Это то, что я имею в виду, когда говорю, что внутренняя работа этой функции уже полностью реализована, все, что необходимо, - это синтаксис инициализации.
На языке C эта функция эмулируется явными смещениями, полученными с помощью стандартного offsetof
макрос. А в C я могу получить offsetof(Outer, inner.i)
и offsetof(Outer, j[2])
. К сожалению, эта возможность не отражается в C ++ указателях на данные.
Как указано в ответе AnT, это, по-видимому, является упущением стандарта и отсутствием правильного синтаксиса для выражения того, что вы хотите сделать. На днях мой коллега столкнулся с этой проблемой и назвал ваш вопрос и его ответ доказательством того, что это невозможно. Ну, мне нравится вызов, и да, это можно сделать ... но его не очень.
Во-первых, важно понимать, что указатель на член в основном является смещением от указателя на структуру [1]. У языка есть оператор смещения, который подозрительно похож на этот, и, что интересно, дает нам выразительность, необходимую для выполнения того, что мы хотим.
Проблема, с которой мы немедленно сталкиваемся, заключается в том, что 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;
to:
bool MyStruct::* toto = MEMBER_POINTER(MyStruct, inner.c);
[1] Хорошо, оговорка: это может быть неверно для всех архитектур / компиляторов, поэтому переносимые разработчики кода отвлекают взгляд сейчас!
InnerStruct, о котором вы заботитесь, содержится в экземпляре MyStruct, но это не влияет на то, как вы получаете указатель на член InnerStruct.
bool InnerStruct::* toto2 = &InnerStruct::c;
Изменить: перечитывать ваш вопрос, я предполагаю, что вы хотите определить указатель на член внешней структуры и указать его прямо на член внутренней структуры. Это просто не разрешено. Чтобы добраться до члена внутренней структуры, которая содержится во внешней структуре, вам нужно будет создать указатель на внутреннюю структуру itelft, а затем на ее член. Чтобы использовать его, вы бы разделили оба указателя на члены:
// Pointer to inner member of MyStruct:
InnerStruct MyStruct::* toto = &MyStruct::inner;
// Pointer to c member of InnerStruct:
bool InnerStruct::* toto2 = &InnerStruct::c;
// Dereference both to get to the actual bool:
bool x = mystruct.*toto.*toto2;
int (Outer::* ptr_j)[5] = &Outer::j;
. Предполагая, что какой-то экземпляр x
из Outer
, вы бы разыменовали его так: (x.*ptr_j)[3] = 1;
.
– Jerry Coffin
9 February 2012 в 18:57
sizeof(Outer::inner.i)
иdecltype(Outer::inner.i)
. Интересно, в чем причина, которая делает элементы указателей на такие подэлементы более сложными? – Johannes Schaub - litb 19 December 2009 в 00:22Outer o; int (Outer::*p_array)[10]; p_array = &Outer::j; o.*p_array[3] = 0;
– Grim Fandango 10 February 2012 в 12:25