Странное правило С++ для указателей функций-членов?

Возможный дубликат:
Ошибка с адресом в скобках функция-член

В этом недавнем вопросе OP столкнулся со странным положением языка C++, которое делает незаконным взять адрес функции-члена, если имя этой функции-члена заключено в скобки. Например, этот код является незаконным:

struct X {
    void foo();
};

int main() {
    void (X::* ptr)();
    ptr = &(X::foo);   // Illegal; must be &X::foo
}

Я просмотрел это и обнаружил, что это связано с §5.3.1/3 спецификации C++ ISO, которая гласит:

Указатель на элемент формируется только тогда, когда используется явное & и его операнд является квалифицированным идентификатором, не заключенным в скобки [...]

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


person templatetypedef    schedule 20.08.2011    source источник
comment
@Hans: Не дубликат. OP на самом деле является ответом на этот вопрос с самым высоким рейтингом, который спрашивал в чем проблема. Этот вопрос спрашивает почему.   -  person Nemo    schedule 21.08.2011
comment
@Troubadour: ПОЧЕМУ что-то отличается от ЧТО и КАК. например, какой вопрос, какие брюки носит Дональд Дак? есть ответ, что он совершенно голый в нижней части тела, но если вы спросите почему-вопрос, почему Дональд Дак совершенно голый в нижней части тела, то это будет другой ответ, а именно, он УТКА. Короче говоря, если вы спросите, какие брюки носит Дональд Дак, то не правильным ответом будет сказать, что он УТКА. Как видите, 2 diff q с 2 diff a. Ваше здоровье,   -  person Cheers and hth. - Alf    schedule 21.08.2011
comment
@Troubador - я не хотел, чтобы это было дублированием вашего вопроса. Моя интерпретация вашего вопроса заключалась в том, что является основной причиной этой проблемы, а не в том, почему С++ структурирован таким образом, поэтому я задал этот вопрос, чтобы получить ответ на последний вопрос. Прошу прощения, если я неправильно понял ваш первоначальный вопрос и повторил его здесь.   -  person templatetypedef    schedule 21.08.2011
comment
Первоначальный вопрос был почему. templatetypedef, похоже, неверно истолковал это как что, что было глупо, потому что первоначальный вопрос был: в чем причина того, что нельзя использовать круглые скобки при получении адреса нестатической функции-члена? Это очень обман.   -  person Lightness Races in Orbit    schedule 21.08.2011
comment
Да, это дублирующий вопрос.   -  person Johannes Schaub - litb    schedule 21.08.2011
comment
@templatetypedef: Это был вопрос не Трубадора. У Трубадора вопросов нет.   -  person Lightness Races in Orbit    schedule 21.08.2011
comment
Кто-нибудь знает, почему в спецификации есть это правило? Почему бы и нет? Это просто синтаксис, так что это произвольно.   -  person curiousguy    schedule 01.11.2011


Ответы (2)


Это всего лишь личное мнение. Если &(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), компилятору не нужно будет обрабатывать тип члена. Поскольку тип члена всегда обрабатывался в виде указателя на него, я думаю, стандарт отдавал приоритет небольшому упрощению системы типов компилятора.

person Ise Wisteria    schedule 20.08.2011
comment
+1 я думаю, ты прав. :-) - person Cheers and hth. - Alf; 21.08.2011
comment
@AlfP.Steinbach: Вау! Это очень обнадеживает :-D - person Ise Wisteria; 21.08.2011
comment
Потрясающий! Это похоже на правильный ответ. Спасибо вам большое за размещение этого! - person templatetypedef; 21.08.2011
comment
@templatetypedef: О, совсем нет! Рад, что ответ помог :-) - person Ise Wisteria; 21.08.2011
comment
Помните, что вы можете сказать sizeof A::i или sizeof(A::i + 42) в C++11. И вы можете вызвать нестатическую функцию, заключив имя в круглые скобки: struct A { void f() { } void g() { (f)(); } }; (здесь f имеет тип void()). - person Johannes Schaub - litb; 21.08.2011
comment
@JohannesSchaub: Спасибо за указание! В дополнение к тому, что вы прокомментировали, новый стандарт, похоже, разрешает идентификатор типа, например int()const. Это похоже на тип функции, не являющийся членом, но имеет квалификатор cv для this. Если бы меня спросили вот так This denotes a member (function) type, not a pointer to member type, right?, я бы не смог нормально ответить. Поскольку я не очень хорошо знаком с C++0x(11?), я написал ответ в форме прошедшего времени. - person Ise Wisteria; 21.08.2011

Представьте себе этот код:

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 - нехорошо).


Из АРМ:

Обратите внимание, что оператор адреса должен использоваться явно для получения указателя на элемент; неявного преобразования нет... Если бы оно было, у нас была бы неоднозначность в контексте функции-члена... Например,

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 просто сохранил эту предстандартную концепцию и прямо упомянул, что круглые скобки делают так, что вы не получаете указатель на элемент.

person Johannes Schaub - litb    schedule 21.08.2011