聊一上C++给自己挖的坑~

聊一下C++给自己挖的坑~~~
C++标准中明确说,通过"指针"或"引用"使用对象的虚函数,对象有动态行为.
通过对象本身直接调用虚函数则不在此列.它直接调用虚函数的final overrider.

也就是说,
A a;
// 假设foo是虚函数
a.foo() // 这时没有动态行为
A *pa = &a;
pa->foo(); // 这时有动态行为

那如果
(&a)->foo(); // 这样会怎么样?
从语法上来讲,这是希望通过指针使用对象的虚函数.但几乎所有编译器把这句都优化成为
a.foo();
从结果来看,这样无法使用对象的动态行为.(不过GCC,CLANG在某些上下文中,使用O0优化可以得到动态行为)
这和标准是有冲突的.因为我没能在标准里找到关于这种优化的明确说明.
难道这是从this行为反推出来的?
在标准里,讲虚函数行为时,有指出,省略this的虚成员调用具有动态行为.而在说成员函数调用时,又指出,省略this
的成员函数调用相当于(*this).member这种形式.联系成员的动态行为,推出(*this).member等价于this->member.
于是乎,反推出(&obj)->func等价于obj.func
在现实中,也确实是这样,几乎所有的编译器都使用这种行为.

大家都可以看出来,在这种上下文中,a.foo无论动态/静态,都没有关系.因为这里名称a是直接绑定到对象的.动态行为
实际上就是静态行为.我猜当初标准的设计也是这么想的,所以a.foo这类调用被赋予了静态行为,从而更高效.否则也
没必要发明final overrider这个概念了.
现在问题来了,标准中有这么一种用法
A *pa;
...
pa->~A();
new(pa) B;
手工析构一个对象,然后在那个对象的地址上重新创建另一个对象.只要对象的大小能保证,行为是明确的.
如果用这种办法,把前面例子中的a变成它的派生类B的对象,问题就来了.
这里a绑定的对象,实际已经是B了.但a.foo还是具有静态的行为,pa->foo却会有动态行为.一些"聪明"的编译器发现pa直接
引用自a,还会自动把pa->foo变成了a.foo ...
由于栈中对象的析构过程是静态绑定的,所以,就算A有虚析构函数也不顶事.a在从栈中析构时,还是被当做A的实例,而非B.

假如a.foo同样是动态调用,以上所有的问题就迎刃而解了.也没必要为了统一静态动态行为而发明final overrider,以及
与其配套的相关规则了.反正都没有完全统一.结果这种虚函数静态调用机制,既和虚函数动态调用机制行为不同,又和非虚
函数静态调用机制不同(这个不同请参考using不覆盖final overrider的规则).

果然过早的优化是一种罪恶~~

------解决方案--------------------
对well-formed的程序, C++ 允许编译器进行优化, 优化的前提是保证可见行为不变. 

没发现你说的有什么坑,也没发现你说的编译器有哪个不合标准的行为.

而且, 编译器不合标准也不是C++ 的坑,你直接找编译器厂商理论去..

------解决方案--------------------
在标准里,讲虚函数行为时,有指出,省略this的虚成员调用具有动态行为.而在说成员函数调用时,又指出,省略this
的成员函数调用相当于(*this).member这种形式.联系成员的动态行为,推出(*this).member等价于this->member.
于是乎,反推出(&obj)->func等价于obj.func
在现实中,也确实是这样,几乎所有的编译器都使用这种行为.
================================================================================
你这些推来推去都没有什么道理可言, 编译器优化是要根据上下文来的, 不是指根据某一条语句就能做优化的. 在某个地方的 (&a)->foo(); 被优化成了静态绑定, 并不代表所有的 (&a)->foo(); 语句都变成静态绑定了. 编译器在不改变代码行为的情况下可以做任何的优化, 甚至 (&a)->foo(); 优化来没有函数调用也没什么错, 只要优化后的代码产生的结果和不优化产生的结果一样就行.