Old article, I forgot to post it in segmentfault: Original

Wrong demonstration

Push_back is wrong to write like this:

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();
    }
  }
};//

Entering the parameter T t causes the caller to make a copy of the temporary object TMP here, and later the TMP will be implicitly destructed at the function exit point. so this wording is not well- .

std::move(t) in the function body, it is better than nothing. It does not reduce the copy of TMP for t , but only one copy t to _data

Working right

When developing template classes, we often encounter push_back scenarios.

The correct push_back should contain two semantics: lvalue copy and rvalue move, generally speaking like this:

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();
    }
  }
};

Note that rvalue plus move semantics is a pair. T t and move semantics together is just an illusion .

You can also add a push_back movement semantics:

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

This is because according to the convention, emplace_back usually uses template variable parameters and implements the in-situ construction of the T class. this topic in 16132ef4c585c0 C++ in-situ constructor and perfect forwarding-writing our own variant wrapper class , so I won’t go into details here.

X-class

hicc::debug::X is a special tool for debugging RVO, In-place construction, Copy Elision and other features. It is unremarkable, just embedding ice in several places to print stdout text, which allows us to visually observe which The behavior actually happened.

X-class has a similar structure in the input part of the constructor:

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

:end:


hedzr
95 声望19 粉丝