Странное правило С++ для указателей на функцию-член?
Возможный дубликат:
Ошибка с адресом круглой функции-члена
В этот недавний вопрос OP столкнулся с странным положением языка С++, что делает его незаконным для принятия адреса функции-члена, если это имя функции-члена круглые скобки. Например, этот код является незаконным:
struct X {
void foo();
};
int main() {
void (X::* ptr)();
ptr = &(X::foo); // Illegal; must be &X::foo
}
Я просмотрел это и обнаружил, что он из-за § 5.3.1/3 спецификации С++ ISO, который читает
Указатель на член формируется только тогда, когда явный и используется, а его операнд - это идентификатор, не заключенный в круглые скобки [...]
Есть ли у кого-нибудь идеи, почему спецификация имеет это правило? Это специфично для указателей на член, поэтому я бы заподозрил, что есть некоторая грамматическая двусмысленность, которая это решает, но я, честно говоря, не имею ни малейшего представления о том, что это может быть.
Ответы
Ответ 1
Это просто личное мнение.
Если &(qualified-id)
разрешено как &(unary-expression)
,
Квалифицированный идентификатор должен быть выражением, и ожидается, что выражение будет иметь тип
(даже если он неполный).
Однако у С++ не был тип, который обозначает член, только
указатель на элемент.
Например, следующий код не может быть скомпилирован.
struct A { int i; };
template< class T > void f( T* );
int main() {
(void) typeid( A::i );
f( &A::i );
}
Чтобы сделать допустимым &(qualified-id)
, компилятор должен удержаться
тип элемента внутри.
Однако, если мы откажемся от обозначения &(qualified-id)
, компилятору не нужно
для управления типом элемента.
Поскольку тип члена всегда обрабатывался в виде указателя на него,
Я полагаю, что стандарт дал приоритет упрощению типа компилятора
системы немного.
Ответ 2
Представьте этот код:
struct B { int data; };
struct C { int data; };
struct A : B, C {
void f() {
// error: converting "int B::*" to "int*" ?
int *bData = &B::data;
// OK: a normal pointer
int *bData = &(B::data);
}
};
Без трюка с круглыми скобками вы не сможете взять указатель непосредственно на элемент данных B (вам понадобятся броски базового класса и игры с this
- не приятно).
Из ARM:
Обратите внимание, что адрес-оператор должен быть явно использован для получения указателя на элемент; нет никакого неявного преобразования... Если бы было, у нас была бы двусмысленность в контексте функции-члена... Например,
void B::f() {
int B::* p = &B::i; // OK
p = B::i; // error: B::i is an int
p = &i; // error: '&i'means '&this->i' which is an 'int*'
int *q = &i; // OK
q = B::i; // error: 'B::i is an int
q = &B::i; // error: '&B::i' is an 'int B::*'
}
IS просто сохранил эту концепцию pre-Standard и прямо упомянул, что круглые скобки делают это так, что вы не получаете указатель на член.