Подтвердить что ты не робот

Typeid не работает с нестатической функцией-членом

clang не компилирует третий вызов typeid ниже (см. живой пример). Но я не вижу ничего в п. 5.2.8, который запрещает это, особенно если учесть, что выражение B::f не является glvalue типа полиморфного класса (см. Пункт 3). Кроме того, согласно этому параграфу выражение B::f является неоцененным операндом, и как таковой, вызов typeid(B::f) должен компилироваться. Обратите внимание, что GCC не компилирует ни один из вызовов typeid ниже:

#include <iostream>
#include <typeinfo>

struct A{ int i; };
struct B{ int i; void f(); };

int main()
{
    std::cout << typeid(A::i).name() << '\n';
    std::cout << typeid(B::i).name() << '\n';
    std::cout << typeid(B::f).name() << '\n';
}
4b9b3361

Ответ 1

Насколько я могу сказать, clang верен, использование нестатического члена действительно только в неоцененном контексте, если он является членом данных. Таким образом, похоже, что gcc неверен для первых двух случаев, но gcc работает правильно в случае sizeof и decltype, которые также имеют неоцененные операнды.

Из черновик сценария С++ 11 5.1.1 [expr.prim.general]:

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

и включает следующую марку:

если это id-выражение обозначает нестатический элемент данных и появляется в неоцененном операнде. [Пример:

struct S {
    int m;
};
int i = sizeof(S::m); // OK
int j = sizeof(S::m + 42); // OK 

-end пример]

Остальные пули не применяются, они выглядят следующим образом:

  • как часть доступа члена класса (5.2.5), в котором выражение объекта ссылается на класс участников 61 или класс, полученный из этого класс или
  • для формирования указателя на элемент (5.3.1) или
  • в mem-initializer для конструктора для этого класса или для класса, полученного из этого класса (12.6.2), или
  • в скобках или равных инициализаторах для нестатического элемента данных этого класса или класса, полученного из этого класса (12.6.2), или

Мы знаем, что операнд не оценивается из раздела 5.2.8, который гласит:

Когда typeid применяется к выражению, отличному от значения gl полиморфный тип класса, [...] Выражение - неоцененный операнд (Пункт 5).

Из грамматики видно, что id-выражение является либо неквалифицированным-id, либо квалифицированным-id:

id-expression:
    unqualified-id
    qualified-id

Обновить

Подано сообщение gcc : typeid не позволяет id-выражению, которое обозначает нестатический элемент данных.

Ответ 2

typeid(A::i).name() не совсем делает то, что я думал, что это будет делать. Я ожидал, что это будет указатель-член, но на самом деле это просто int.

Чтобы увидеть это, запустите этот код:

#include <iostream>
struct A{ int i; };
struct B{ int i; void f(void); };

template<typename T>
void what_is_my_type() {
    std:: cout << __PRETTY_FUNCTION__ << std:: endl;
}

int main()
{
    what_is_my_type<decltype(&A::i)>(); // "void what_is_my_type() [T = int A::*]"
    what_is_my_type<decltype(&B::i)>(); // "void what_is_my_type() [T = int B::*]"
    what_is_my_type<decltype(&B::f)>(); // "void what_is_my_type() [T = void (B::*)()]"

    what_is_my_type<decltype(A::i)>();  // "void what_is_my_type() [T = int]"
    what_is_my_type<decltype(B::i)>();  // "void what_is_my_type() [T = int]"
    // what_is_my_type<decltype(B::f)>();       //    doesn't compile

}

Я помещал вывод в комментарий после каждого вызова.

Первые три вызова работают как ожидалось - все три работы и информация о типе включают тип структуры (A или B), а также тип члена.

Последние три разные. Последний не компилируется, а первые два просто печатают int. Я думаю, что это ключ к тому, что не так. Для конкретного адреса A или B можно указать адрес этого конкретного члена:

A a;
int * x = &(a.i);
*x = 32;

но это невозможно (или даже значимо?) для этого:

B b;
???   y = &(a.f); // what does this even mean?

Наконец, чтобы подчеркнуть, что речь идет не о указателях, рассмотрим это:

A a;
B b;
int x = a.i;
int y = b.i;
??? z = b.f;  // what would this mean? What its type?