typeid 不适用于非静态成员函数

typeid doesn't work with non-static member function

clang 不会编译下面对 typeid 的第三次调用(参见 live example)。但是我在 §5.2.8 中看不到任何不允许这样做的内容,特别是当我们认为表达式 B::f 不是多态 class 类型的泛左值时(参见第 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';
}

据我所知 clang 是正确的,如果非静态成员是数据成员,则仅在未计算的上下文中使用该成员才有效。所以看起来 gcc 对于前两种情况是不正确的,但是 gccsizeofdecltype 的情况下工作正常,它们也有未计算的操作数。

来自 draft C++11 standard 部分 5.1.1 [expr.prim.general]:

An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

并包括以下项目符号:

if that id-expression denotes a non-static data member and it appears in an unevaluated operand. [ Example:

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

—end example ]

其余项目符号不适用,如下:

  • as part of a class member access (5.2.5) in which the object expression refers to the member’s class61 or a class derived from that class, or
  • to form a pointer to member (5.3.1), or
  • in a mem-initializer for a constructor for that class or for a class derived from that class (12.6.2), or
  • in a brace-or-equal-initializer for a non-static data member of that class or of a class derived from that class (12.6.2), or

我们从 5.2.8 部分知道操作数未计算,它说:

When typeid is applied to an expression other than a glvalue of a polymorphic class type, [...] The expression is an unevaluated operand (Clause 5).

我们可以从语法中看出,id-expression要么是unqualified-id,要么是qualified -id:

id-expression:
    unqualified-id
    qualified-id

更新

提交了 gcc bug report: typeid does not allow an id-expression that denotes a non-static data member

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

}

我在每次调用后将输出放在评论中。

前三个调用按预期工作 - 所有三个调用都工作,类型信息包括结构类型(AB)以及成员类型。

虽然最后三个不同。最后一个甚至没有编译,前两个只是打印 int。我认为这是关于错误的线索。给定一个特定的 AB,可以获取该特定成员的地址:

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's its type?