CppCon 2017: Nicolai Josuttis “The Nightmare of Move Semantics for Trivial Classes”
Why Stack Overflow recommends it
Stack Overflow users link it when answering how to write constructors and setters that take strings, and whether to pass by value and std::move or use forwarding references.
Recommended by 9 people across 9 different months, 2017 to 2022.
What it is
A CppCon 2017 talk by Nicolai Josuttis that uses live coding on a trivial class with several string members. He compares constructors taking arguments by value, by reference, by perfect forwarding and with enable_if, counting allocations, copies and moves for each.
What people said
Strong rec PaulR · 2018-02 · Stack Overflow · on “Why copy constructor is being called two times in below code?”
The first copy is from a1 to the parameter a of the MyClass constructor, the second one is from the parameter a to the member var . If you want to reduce the number of copies, you have (at least) two options: Pass a const reference to the constructor as mentioned by Joris: MyClass(A const &a) : var(a) { } If A can be efficiently moved, take a by value and move to the member: MyClass(A a) : var(std::move(a)) { } That way, a user that does not need his A anymore can move it into the new MyClass instance, while a user that still needs the A can pass it by value. void useA(A &a); void doSomethingWithM(MyClass & m); void functionThatNeedsAAgain() { A myImportantA; MyClass m {myImportantA}; // cop
Strong rec Fureeish · 2018-09 · Stack Overflow · on “Pass by value or universal reference”
These are not equivalent and sometimes one is desired over the the other one. A stellar talk by Nicolai Josuttis [this video] is an hour worth of talking just about this. I highly recommend watching it at least once. Personally, unless you encounter a special case, where conversions are expensive and you want to avoid temporaries as much as possible, I would suggest just passing by value and std::move ing the argument. Case where T&& is more efficient: struct foo { std::string bar; template <typename T> foo(T&& t) :bar(std::forward<T>(t)){} }; versus: struct foo { std::string bar; foo(std::string t) :bar(std::move(t)){} }; when you do: int main() { foo f("some char*"); } In the first case (perfec
Recommends MaLarsson · 2018-03 · Stack Overflow · on “Constructor initialization speed”
I would also expect //2 to be the fastest, lets have a look at what the difference constructors do. //1: name_ is default constructed and then copy assigned. //2: name_ is copy constructed. //3: name is copy constructed and then name_ is move constructed. The difference between //1 and //2 will grow as default construction becomes more expensive. //3 is valid since the argument name only lives inside the constructor so we can steal the innards by move constructing name_. Nicolai Josuttis have a great talk [this video] from CppCon 2017 on this.
Recommends JDługosz · 2018-04 · Stack Overflow · on “Are the days of passing const std::string & as a parameter over?”
See “Herb Sutter "Back to the Basics! Essentials of Modern C++ Style” [LINK] . Among other topics, he reviews the parameter passing advice that’s been given in the past, and new ideas that come in with C++11 and specifically looks at the idea of passing strings by value. The benchmarks show that passing std::string s by value, in cases where the function will copy it in anyway, can be significantly slower! This is because you are forcing it to always make a full copy (and then move into place), while the const& version will update the old string which may reuse the already-allocated buffer. See his slide 27: For “set” functions, option 1 is the same as it always was. Option 2 adds an overloa
Recommends Maxim Egorushkin · 2018-08 · Stack Overflow · on “What is the preferred way to initialize a string member in the constructor?”
The most efficient way is to perfect-forward any argument convertible to std::string : class MyStr { std::string m_str; public: template<class T, class = std::enable_if_t<std::is_convertible<std::remove_reference_t<T>, std::string>::value>> MyStr(T&& arg) : m_str(std::forward<T>(arg)) {} }; A bit of a mouthful though. The second best option is to not provide constructors at all, make data members public and initialize MyStr with aggregate initialization syntax. Full story: CppCon 2017: Nicolai Josuttis “The Nightmare of Move Semantics for Trivial Classes” [this video] .
Recommends Martin Morterol · 2019-04 · Stack Overflow · on “Efficient way to construct object with string member”
If you have 1 hour of time, try this CPPCON video ^^. [this video] . TLDR : just by value is pretty much good (not perfect perfect but good)
Recommends jan.sende · 2019-06 · Stack Overflow · on “Overloaded functions neither take objects by value, nor by reference, but take de-referenced pointer to object”
The answers already address your problem. Yet, I still would like to recommend the following video to you, for learning about passing values to functions: [this video]
Recommends non-user38741 · 2020-08 · Stack Overflow · on “How to best define/construct/initialize a std::string wrapper class”
There is a very interesting talk by Josuttis talking about a similar case and the problems of `move` here: [this video]
Recommends Johnie Walker · 2022-06 · Stack Overflow · on “Template for initialization of the "customer class" from Nicolai Josuttis' c++17 presentation”
I just watched a presentation by Nicolai Josuttis “The Nightmare of Move Semantics for Trivial Classes” [this video] In the presentation he shows how to build a "perfect customer class", so that it's constructor accepts up to all three arguments with as few mallocs as possible. Here are the solutions he presents: // 11 mallocs (4cr + 7cp + 1mv) Customer(std::string f = "", std::string l = "", int i = 0) : first(f), last(l), id(i) {} // 5 mallocs (4cr + 1cp + 5mv) Customer(std::string f = "", std::string l = "", int i = 0) : first(std::move(f)), last(std::move(l)), id(i) {} // all manual combinations carefully avoiding ambiguities, such as // 5 mallocs (4cr + 1cp + 1mv) Customer(const std::string&
Cites Nir Friedman · 2017-12 · Stack Overflow · on “Emplace for complex objects”
The reason this doesn't work is not because you can't have implicit conversions. In the following code: vector<B<C>> v; v.emplace_back(5, "Hello"); emplace_back does perfect forwarding to the constructor of the type contained by v , which is B<C> . So we end up with a call to (it actually happens via the allocator, but let's ignore the details): B<C>(5, "Hello") Why doesn't this work? You often hear people say that you can't get implicit conversions when calling templates; this is loosely true. But B<C> 's constructor is not a template; B is a template class which has already been templated out, so B<C> is a normal class and whose constructor is not a template. So implicit conversions are al