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';
}
Ответы
Ответ 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?