c – 在gcc和MSVC中不同地调用函数参数的析构函数

前端之家收集整理的这篇文章主要介绍了c – 在gcc和MSVC中不同地调用函数参数的析构函数前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在将一些C代码从Microsoft Visual Studio移植到 gcc时,我遇到了一个奇怪的bug,我最终归结为:
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. class Foo {
  5. public:
  6. int data;
  7. Foo(int i) : data(i)
  8. {
  9. cout << "Foo constructed with " << i << endl;
  10. }
  11. Foo(const Foo& f) : data(f.data)
  12. {
  13. cout << "copy ctor " << endl;
  14. }
  15. Foo(const Foo&& f) : data(f.data)
  16. {
  17. cout << "move ctor" << endl;
  18. }
  19. ~Foo()
  20. {
  21. cout << "Foo destructed with " << data << endl;
  22. }
  23. };
  24.  
  25. int Bar(Foo f)
  26. {
  27. cout << "f.data = " << f.data << endl;
  28. return f.data * 2;
  29. }
  30.  
  31. int main()
  32. {
  33. Foo f1(10);
  34. Foo f2(Bar(std::move(f1)));
  35. }

如果我使用Microsoft Visual Studio 2015社区编译和运行上述代码,我得到以下输出

  1. Foo constructed with 10
  2. move ctor
  3. f.data = 10
  4. Foo destructed with 10
  5. Foo constructed with 20
  6. Foo destructed with 20
  7. Foo destructed with 10

但是,如果我使用gcc 6.1.1和–std = c 14来编译和运行代码,我得到这个输出

  1. Foo constructed with 10
  2. move ctor
  3. f.data = 10
  4. Foo constructed with 20
  5. Foo destructed with 10
  6. Foo destructed with 20
  7. Foo destructed with 10

gcc调用f的析构函数,Bar()的参数在Bar()返回后,而msvc在返回之前调用析构函数(显然),或至少在构造函数f2之前.根据C标准,什么时候f被破坏?

解决方法

他们没事这取决于.标准似乎不太明确.

[expr.call]/4(这个措辞可以追溯到C98);

The lifetime of a parameter ends when the function in which it is defined returns. The initialization and destruction of each parameter occurs within the context of the calling function.

CWG#1880;

WG decided to make it unspecified whether parameter objects are destroyed immediately following the call or at the end of the full-expression to which the call belongs.

g(和clang)和MSVC的行为都是正确的,实现可以自由选择一种方法.

这一切都说,如果你的代码依赖于这个顺序,我会改变它,使得顺序更确定 – 如你所看到的,它导致了微妙的错误.

这个行为的简化例子是:

  1. #include <iostream>
  2. struct Arg {
  3. Arg() {std::cout << 'c';}
  4. ~Arg() {std::cout << 'd';}
  5. Arg(Arg const&) {std::cout << 'a';}
  6. Arg(Arg&&) {std::cout << 'b';}
  7. Arg& operator=(Arg const&) {std::cout << 'e'; return *this;}
  8. Arg& operator=(Arg&&) {std::cout << 'f'; return *this;}
  9. };
  10. void func(Arg) {}
  11. int main() {
  12. (func(Arg{}),std::cout << 'X');
  13. std::cout << std::endl;
  14. }

Clang和g都产生cXd和MSVC生成cdX.

猜你在找的C&C++相关文章