CppCon 2017: Nicolai Josuttis “The Nightmare of Move Semantics for Trivial Classes”

CppCon · 99K views · 2017 · 57:16

Open on YouTube

C, C++, Rust, Go & systems languagesSoftware performance & optimizationtalk

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