c++ - Visual Studio 2008 下的虚假 C++ 析构函数调用(在 GCC 下不存在)

标签 c++ visual-c++ gcc

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

#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; 
}

在linux下用gcc编译,按预期打印AB~AX~B~A(X之前打印的~A是将值传递给 func 的结果,它创建了一个在函数返回时被破坏的拷贝)。

但是在 windows 下使用 VS2008 编译它会打印 AB~A~AX~B~A - 额外的 ~A 来自哪里?如果复制 xtor 被显式定义 A(A& that) {}; 或者如果析构函数被声明为虚拟(可以说应该是),它就会消失。

最佳答案

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

The initialization that occurs in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and brace-enclosed initializer lists (8.5.1) is called copy-initialization and is equivalent to the form T x = a;

现在是关键点。在 T x = a; 中,如果 anot 一个 T 或派生自 T,那么这相当于 T x = T(a);,并且在概念上使用了一个额外的临时值。 (此临时有资格进行复制省略)。

但是,如果 aT 或派生自 T,则不能有额外的临时性。它与T x(a);.

在本题代码中,由于B来源于A,所以不能有临时的。

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

If the destination type is a (possibly cv-qualified) class type:

  • If the class is an aggregate (8.5.1), and the initializer is a brace-enclosed list, see 8.5.1.
  • If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression(s) as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.
  • Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the destination type. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an imple- mentation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

关于c++ - Visual Studio 2008 下的虚假 C++ 析构函数调用(在 GCC 下不存在),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31572915/

相关文章:

c++ - 更改 MAC 地址,通过注册表不起作用

c - 了解 glibc

c++ - arm-none-eabi-g++ 找不到 stdc++ header

C++ 非常简单的未解析外部

c++ - Visual Studio 2017 允许在构造函数中使用自身初始化引用成员。它真的是合法的 C++ 吗?

c++ - 如何为 msvc9.0 而不是 msvc10.0 构建 boost ?

windows - 如何从 Visual C++ 应用程序执行另一个程序

c++ - 为什么模板 <typename...> 不被识别为可通过模板 <template<typename> typename> 实例化?

c++ - SQLite 绑定(bind)数字数据类型

c++ - 如何指示 CMake 仅使用特定目录中的库