C++里这两个为什么打印出来的不一致?

#include <iostream>
#include <string>
#include <utility>

template <typename Fun> class Guard {
public:
  explicit Guard(Fun &&f) : _fun(std::forward<Fun>(f)), _active(true) {}

  ~Guard() {
    if (_active) {
      _fun();
    }
  }

  void Dismiss() { _active = false; }

  Guard() = delete;
  Guard(const Guard &) = delete;
  Guard &operator=(const Guard &) = delete;

  Guard(Guard &&rhs) : _fun(std::move(rhs._fun)), _active(rhs._active) {
    rhs.Dismiss();
  }

private:
  Fun _fun;
  bool _active;
};

enum class GuardOnExit {};

template <typename Fun> inline Guard<Fun> operator+(GuardOnExit, Fun &&fn) {
  return Guard<Fun>(std::forward<Fun>(fn));
}

// 自动生成唯一标识符
#define XHL_CAT(s1, s2) s1##s2
#define AUTO_NAME_GEN_(name, line) XHL_CAT(name, line)
#define AUTO_NAME_GEN AUTO_NAME_GEN_(AUTO_NAME_, __LINE__)

// @brief auto guard = MAKE_SG{ fclose(fp);};
#define MAKE_SG GuardOnExit() + [&]()
// @brief ON_SCOPE_EXIT{fclose(fp);};
#define ON_SCOPE_EXIT auto AUTO_NAME_GEN = MAKE_SG

std::string GetResult1() {
  std::string result = "";
  ON_SCOPE_EXIT { std::cout << "Result1:" << result << std::endl; };

  std::string gray_result = "test1";
  bool gray = false;
  if (gray) {
    return gray_result;
  }

  result = "Result1";
  return result;
}

std::string GetResult2() {
  std::string result = "";
  ON_SCOPE_EXIT { std::cout << "Result2: " << result << std::endl; };

  std::string gray_result = "test";
  bool gray = false;
  if (gray) {
    // return gray_result;
  }

  result = "Result2";
  return result;
}

int main() {
  std::string result1 = GetResult1();
  std::cout << "result1: " << result1 << std::endl;
  std::string result2 = GetResult2();
  std::cout << "result2: " << result2 << std::endl;
  return 0;
}

打印出来的结果
image.png

  1. 为什么GetResult1里面打印不出来result值,但是实际外层调用是有返回这个值的
    image.png
  2. GetResult2和GetResult1只相差了return gray_result;,但是GetResult2里面能打印出来result值
    image.png
阅读 1.6k
1 个回答

Guard 析构发生在 return result; 之后。return result; 使用 result 构造一个新的临时对象作为返回值(result 就析构了,没法返回调用者),而且会尽可能使用 move 而不是 copy 。这里在打印的时候 result 就已经被 move 了。被 move 之后的对象通常会被置空,所以啥也没有了。

而在 GetReresult2 中,由于少了一个 return ,满足了 copy elision 的条件。发生 copy elision 之后,return 处的 copy/move 就不会发生了。


return 会使用其操作数构造一个临时对象作为返回值,局部变量的析构发生在临时对象构造之后。(stmt.jump#stmt.return

return 一个局部变量会尽可能使用 move ,而不是 copy 。(class.copy.elision#3

被 move 的对象,其状态时不确定的(通常是会被置空)。(lib.types.movedfrom#1

GetResult2 由于少了一个 return ,满足了发生 copy elision 的条件,于是本来时“局部”的 result 变量,可以直接使用 main 中的 result2 ,从而省去了一个 copy / move。(class.copy.elision#1.1

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题