When is a const reference better than pass-by-value in C++11?

前端 未结 5 619
醉梦人生
醉梦人生 2020-12-04 17:13

I have some pre-C++11 code in which I use const references to pass large parameters like vector\'s a lot. An example is as follows:



        
5条回答
  •  星月不相逢
    2020-12-04 17:51

    C++11's move semantics make passing and returning by value much more attractive even for complex objects.

    The sample you give, however, is a sample of pass by value

    int hd(vector a) {
    

    So C++11 has no impact on this.

    Even if you had correctly declared 'hd' to take an rvalue

    int hd(vector&& a) {
    

    it may be cheaper than pass-by-value but performing a successful move (as opposed to a simple std::move which may have no effect at all) may be more expensive than a simple pass-by-reference. A new vector must be constructed and it must take ownership of the contents of a. We don't have the old overhead of having to allocate a new array of elements and copy the values over, but we still need to transfer the data fields of vector.

    More importantly, in the case of a successful move, a would be destroyed in this process:

    std::vector x;
    x.push(1);
    int n = hd(std::move(x));
    std::cout << x.size() << '\n'; // not what it used to be
    

    Consider the following full example:

    struct Str {
        char* m_ptr;
        Str() : m_ptr(nullptr) {}
        Str(const char* ptr) : m_ptr(strdup(ptr)) {}
        Str(const Str& rhs) : m_ptr(strdup(rhs.m_ptr)) {}
        Str(Str&& rhs) {
          if (&rhs != this) {
            m_ptr = rhs.m_ptr;
            rhs.m_ptr = nullptr;
          }
        }
        ~Str() {
          if (m_ptr) {
            printf("dtor: freeing %p\n", m_ptr)
            free(m_ptr);
            m_ptr = nullptr;
          }
        }
    };
    
    void hd(Str&& str) {
      printf("str.m_ptr = %p\n", str.m_ptr);
    }
    
    int main() {
      Str a("hello world"); // duplicates 'hello world'.
      Str b(a); // creates another copy
      hd(std::move(b)); // transfers authority for b to function hd.
      //hd(b); // compile error
      printf("after hd, b.m_ptr = %p\n", b.m_ptr); // it's been moved.
    }
    

    As a general rule:

    • Pass by value for trivial objects,
    • Pass by value if the destination needs a mutable copy,
    • Pass by value if you always need to make a copy,
    • Pass by const reference for non-trivial objects where the viewer only needs to see the content/state but doesn't need it to be modifiable,
    • Move when the destination needs a mutable copy of a temporary/constructed value (e.g. std::move(std::string("a") + std::string("b"))).
    • Move when you require locality of the object state but want to retain existing values/data and release the current holder.

提交回复
热议问题