Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
181 views
in Technique[技术] by (71.8m points)

c++ - Advantages of pass-by-value and std::move over pass-by-reference

I'm learning C++ at the moment and try avoid picking up bad habits. From what I understand, clang-tidy contains many "best practices" and I try to stick to them as best as possible (even though I don't necessarily understand why they are considered good yet), but I'm not sure if I understand what's recommended here.

I used this class from the tutorial:

class Creature
{
private:
    std::string m_name;

public:
    Creature(const std::string &name)
            :  m_name{name}
    {
    }
};

This leads to a suggestion from clang-tidy that I should pass by value instead of reference and use std::move. If I do, I get the suggestion to make name a reference (to ensure it does not get copied every time) and the warning that std::move won't have any effect because name is a const so I should remove it.

The only way I don't get a warning is by removing const altogether:

Creature(std::string name)
        :  m_name{std::move(name)}
{
}

Which seems logical, as the only benefit of const was to prevent messing with the original string (which doesn't happen because I passed by value). But I read on CPlusPlus.com:

Although note that -in the standard library- moving implies that the moved-from object is left in a valid but unspecified state. Which means that, after such an operation, the value of the moved-from object should only be destroyed or assigned a new value; accessing it otherwise yields an unspecified value.

Now imagine this code:

std::string nameString("Alex");
Creature c(nameString);

Because nameString gets passed by value, std::move will only invalidate name inside the constructor and not touch the original string. But what are the advantages of this? It seems like the content gets copied only once anyhow - if I pass by reference when I call m_name{name}, if I pass by value when I pass it (and then it gets moved). I understand that this is better than passing by value and not using std::move (because it gets copied twice).

So two questions:

  1. Did I understand correctly what is happening here?
  2. Is there any upside of using std::move over passing by reference and just calling m_name{name}?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
/* (0) */ 
Creature(const std::string &name) : m_name{name} { }
  • A passed lvalue binds to name, then is copied into m_name.

  • A passed rvalue binds to name, then is copied into m_name.


/* (1) */ 
Creature(std::string name) : m_name{std::move(name)} { }
  • A passed lvalue is copied into name, then is moved into m_name.

  • A passed rvalue is moved into name, then is moved into m_name.


/* (2) */ 
Creature(const std::string &name) : m_name{name} { }
Creature(std::string &&rname) : m_name{std::move(rname)} { }
  • A passed lvalue binds to name, then is copied into m_name.

  • A passed rvalue binds to rname, then is moved into m_name.


As move operations are usually faster than copies, (1) is better than (0) if you pass a lot of temporaries. (2) is optimal in terms of copies/moves, but requires code repetition.

The code repetition can be avoided with perfect forwarding:

/* (3) */
template <typename T,
          std::enable_if_t<
              std::is_convertible_v<std::remove_cvref_t<T>, std::string>, 
          int> = 0
         >
Creature(T&& name) : m_name{std::forward<T>(name)} { }

You might optionally want to constrain T in order to restrict the domain of types that this constructor can be instantiated with (as shown above). C++20 aims to simplify this with Concepts.


In C++17, prvalues are affected by guaranteed copy elision, which - when applicable - will reduce the number of copies/moves when passing arguments to functions.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...