这是一个例子:
struct a {
virtual int bar();
};
struct foo : public virtual a {
};
void test(foo *P) {
return P->bar()+*P;
}
Clang 产生:
t.cc:9:18: error: invalid operands to binary expression ('int' and 'foo')
return P->bar()+*P;
~~~~~~~~^~~
GCC 4.2 产生:
t.cc: In function ‘void test(foo*)’:
t.cc:9: error: no match for ‘operator+’ in ‘(((a*)P) + (*(long int*)(P->foo::<anonymous>.a::_vptr$a + -0x00000000000000020)))->a::bar() + * P’
t.cc:9: error: return-statement with a value, in function returning 'void'
GCC 这样做是因为它的 C++ 前端在许多情况下都固定在 C 前端之上。解析器没有为各种 C++ 操作构建特定于 C++ 的抽象语法树 (AST),而是立即将它们降低到它们的 C 等价物。在这种情况下,GCC 会合成一个包含 vtable 的结构体,然后将对 bar 的指针解引用降低为一系列 C 指针解引用、强制转换、指针算术等。
Clang 没有这个问题,因为它有一个非常干净的 AST,直接代表源代码。如果您将示例更改为:
struct a {
virtual int bar();
};
struct foo : public virtual a {
};
void test(foo *P) {
P->bar();
}
.. 使代码有效,然后让 clang 用 "clang -cc1 -ast-dump t.cc" 转储它的 ast,你得到:
...
void test(foo *P)
(CompoundStmt 0x10683cae8 <t.cc:8:19, line:10:1>
(CXXMemberCallExpr 0x10683ca78 <line:9:3, col:10> 'int'
(MemberExpr 0x10683ca40 <col:3, col:6> '<bound member function type>' ->bar 0x10683bef0
(ImplicitCastExpr 0x10683cac8 <col:3> 'struct a *' <UncheckedDerivedToBase (virtual a)>
(ImplicitCastExpr 0x10683ca28 <col:3> 'struct foo *' <LValueToRValue>
(DeclRefExpr 0x10683ca00 <col:3> 'struct foo *' lvalue ParmVar 0x10683c8a0 'P' 'struct foo *'))))))
-克里斯