Moving parameter to data member: Take parameter by copy or rvalue-ref?

94 Views Asked by At

I have a class object entity which gobbles up a string and shoves it into it's member on construction (for the sake of this argument this could be any old member function).

Now I can do this in at least two ways: I might accept a std::string copy and move this into a member (inefficient_entity). However I could also directly take by rvalue-ref and just continue the move into the data member (efficient_entity).

Is there a performance difference between the two? I'm asking bc it's way more convenient to take by copy and let the call site decide if it wants to move or copy the string. The other way I would probably need to create an overload set which can grow to huge amounts given that my constructor could also accept multiple arguments in the same manner.

Will this be optimized out anyway or do I have to worry?

Demo

#include <string>
#include <cstdio>

struct efficient_entity
{
    efficient_entity(std::string&& str)
        :   str_ { std::move(str) }
    { }

    std::string str_;
};

struct inefficient_entity
{
    inefficient_entity(std::string str)
        :   str_ { std::move(str) }
    { }

    std::string str_;
};


int main()
{
    std::string create_here = "I guarantee you this string is absolutely huge!";
    efficient_entity(std::move(create_here));
    inefficient_entity(std::move(create_here));
}

Note: Afaik there was a talk on cppcon which covered exactly that but I can't find it anymore (appreciated if someone could drop a hint).

1

There are 1 best solutions below

0
HolyBlackCat On

Passing by value costs you one extra move.

Passing by reference, on the other hand, requires you to write two functions: const T & or T &&. Or 2N functions for N parameters, or a template.

I prefer the first one by default, and use the second one in low-level utilities that should be fast.


Having just a single T && overload is viable, but highly unorthodox. This gives you one extra move only for copies, which have to be written as func(std::string(value)) (or func(auto(value)) in C++23).

The supposed benefit here is that all copies are explicit. I would only do this for heavy types, and only if you like this style.