乐趣区

关于c++:C-复制消除问题

旧文章,遗记了发在 segmentfault:原文

谬误示范

push_back 这么写是错的:

template<class T>
  class threaded_message_queue {
    public:
    using lock = std::unique_lock<std::mutex>;
    void push_back(T t) {
      {lock l(_m);
        _data.push_back(std::move(t));
      }
      _cv.notify_one();}
  }
};//

入参 T t 导致了调用者在这里会产生一次长期对象 TMP 的复制,稍后在函数退出点处 TMP 还会被隐式析构。 所以这个写法不是良构

至于函数体中的 std::move(t) 也就是聊胜于无了,它并不会让 t 少掉 TMP 的复制,仅仅只是少掉了 t_data 的一次复制而已。

正确工作

做模板类开发时,常常会遇到 push_back 的这种场景。

正确的 push_back 应该蕴含左值复制和右值挪动两种语义,一般来说像是这样子:

template<class T>
  class threaded_message_queue {
    public:
    using lock = std::unique_lock<std::mutex>;
    void emplace_back(T &&t) {
      {lock l(_m);
        _data.template emplace_back(std::move(t));
      }
      _cv.notify_one();}
    void push_back(T const &t) {
      {lock l(_m);
        _data.push_back(t);
      }
      _cv.notify_one();}
  }
};

留神右值加上挪动语义才是一对搭配。T t 和挪动语义在一起只是一种错觉

你还能够加上一个 push_back 的挪动语义:

    void push_back(T &&t) {
      {lock l(_m);
        _data.template emplace_back(std::move(t));
      }
      _cv.notify_one();}

这是因为依照约定,emplace_back 通常采纳模板变参并实现 T 类的原位结构。这个话题我在 C++ 中的原位构造函数及完满转发 – 写咱们本人的 variant 包装类 曾经有过肯定的探讨,这里就不详述了。

X-class

hicc::debug::X 是一个专门用来调试 RVO,In-place construction,Copy Elision 等等个性的工具类,它平平无奇,只不过是在若干地位埋点冰打印 stdout 文字而已,这能够让咱们直观察看到哪些行为实际上产生了。

X-class 在构造函数的入参局部有类似的结构:

namespace hicc::debug {

    class X {
        std::string _str;

        void _ct(const char *leading) {printf("- %s: X[ptr=%p].str: %p,'%s'\n", leading, (void *) this, (void *) _str.c_str(), _str.c_str());
        }

    public:
        X() {_ct("ctor()");
        }
        ~X() {_ct("dtor");
        }
        X(std::string &&s)
            : _str(std::move(s)) {_ct("ctor(s)");
        }
        X(std::string const &s)
            : _str(s) {_ct("ctor(s(const&))");
        }
        X &operator=(std::string &&s) {_str = std::move(s);
            _ct("operator=(&&s)");
            return (*this);
        }
        X &operator=(std::string const &s) {
            _str = s;
            _ct("operator=(const&s)");
            return (*this);
        }

        const char *c_str() const { return _str.c_str(); }
        operator const char *() const { return _str.c_str(); }
    };

} // namespace hicc::debug

退出移动版