Visual Studio 2008下的虚假C++析构函数调用(GCC下没有)

Sca*_*kel 12 c++ gcc visual-c++

采用以下(设计的)类层次结构,从构造函数和析构函数打印到控制台:

#include <iostream>

class A { 
public:
  A() { std::cout << "A"; }
  ~A() { std::cout << "~A"; } 
};

class B : public A { 
public:   
  B() { std::cout << "B"; }
  ~B() { std::cout << "~B"; } 
};

void func(A a) { }

int main() {   
  B b;
  func(b);
  std::cout << "X";
  return 0; 
}
Run Code Online (Sandbox Code Playgroud)

在linux下用gcc编译,它AB~AX~B~A按预期~A打印(之前打印的X是pass-by-value的结果,func它创建了一个在函数返回时被破坏的副本).

但是在Windows下使用VS2008编译它会打印AB~A~AX~B~A- 额外的来源~A是什么?如果明确定义了副本xtor,A(A& that) {};或者析构函数被声明为虚拟(因为它应该是,可以说是),它就会消失.

M.M*_*M.M 5

评论表明,MSVC 2008使用临时g ++不会传递参数.如果是这样,这是一个错误.来自C++ 03 [dcl.init]/12:

参数传递,函数返回,抛出异常(15.1),处理异常(15.3)和大括号括起初始化列表(8.5.1)时发生的初始化称为复制初始化,相当于表单 T x = a;

现在这是至关重要的一点.在T x = a;,如果a不是一个T或衍生自T,那么这相当于T x = T(a);,和一个额外的临时概念性使用.(此临时资格有资格获得复制).

但是,如果a是a T或派生自的T,那么一定不能有额外的临时性.它是一样的T x(a);.

在这个问题的代码中,由于B源自A,所以一定不能是临时的.

C++ 03中的支持文本在[dcl.init]/14下(我已经突出显示了与此问题的代码示例相关的部分):

如果目标类型是(可能是cv限定的)类类型:

  • 如果类是聚合(8.5.1),并且初始化程序是括号括起的列表,请参见8.5.1.
  • 如果初始化是直接初始化,或者它是复制初始化,其中源类型的cv-nonqualified版本与目标类相同的类或派生类,则考虑构造函数.列举了适用的构造函数(13.3.1.3),并通过重载解析(13.3)选择最佳构造函数.调用如此选择的构造函数来初始化对象,初始化表达式作为其参数.如果没有构造函数适用,或者重载决策是不明确的,则初始化是错误的.
  • 否则(即,对于剩余的复制初始化情况),可以如13.3中所述枚举可以从源类型转换为目的地类型或(当使用转换函数时)到其派生类的用户定义的转换序列. 1.4,通过重载决策(13.3)选择最好的一个.如果转换不能完成或不明确,则初始化是错误的.选择的函数以初始化表达式作为参数调用; 如果函数是构造函数,则调用初始化临时函数目的地类型.然后,根据上面的规则,调用的结果(对于构造函数的情况是临时的)用于直接初始化作为复制初始化目标的对象.在某些情况下,允许实现通过将中间结果直接构造到正在初始化的对象中来消除此直接初始化中固有的复制; 见12.2,12.8.

  • 无需延期; 只需转到[Microsoft Connect](https://connect.microsoft.com/VisualStudio/)并单击"提交错误".感谢您报告此问题! (2认同)