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
对于前两种情况是不正确的,但是 gcc
在 sizeof
和 decltype
的情况下工作正常,它们也有未计算的操作数。
来自 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
}
我在每次调用后将输出放在评论中。
前三个调用按预期工作 - 所有三个调用都工作,类型信息包括结构类型(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's its type?
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
对于前两种情况是不正确的,但是 gcc
在 sizeof
和 decltype
的情况下工作正常,它们也有未计算的操作数。
来自 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
}
我在每次调用后将输出放在评论中。
前三个调用按预期工作 - 所有三个调用都工作,类型信息包括结构类型(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's its type?