I try to figure out, lets say I have a buffer of chars in memory that holds a c_string, & I want to add an object of std::string
with the content of that c_string to a std container, lets say: std::list<std::string>
in an efficient way.
Example:
#include <list>
#include <string>
int main()
{
std::list<std::string> list;
char c_string_buffer[] {"For example"};
list.push_back(c_string_buffer); // 1
list.emplace_back(c_string_buffer); // 2
list.push_back(std::move(std::string(c_string_buffer))); // 3
}
I use ReSharper C & it complains about: 1 (and suggests: 2).
When I read this stackoveflow thread: "push_back vs emplace_back", it says that when it is not a rvalue, the container will store a "copied" copy, not a moved copy. Means: 2 does the same as 1. Here also about that: "Don’t blindly prefer emplace_back to push_back"
Case 3: When I read this stackoveflow thread: "What's wrong with moving?", it says that what std::move()
does "is a cast to a rvalue reference, which may enable moving under some conditions".
--
Does 3 actually gives a benefit? I assume that the constructor of std::string
is called & creates an std::string
object with the content of the c_string. I am not sure if later the container constructs another std::string & copy the 1st to the 2nd.
I will appreciate, a short clarification, of the recommended implementation. Thanks in advance.
CodePudding user response:
// 3
is fully equivalent to // 1
. std::move
does absolutely nothing here since std::string(c_string_buffer)
is already a rvalue.
The problem with push_back
is not related to move vs copy.
push_back
is always a bad choice if you don't yet have an object of the element type because it always creates the new container element via copy or move construction from another object of the element type.
If you write list.push_back(c_string_buffer); // 1
, then because push_back
expects a std::string&&
argument (or const std::string&
), a temporary object of type std::string
will be constructed from c_string_buffer
and passed-by-reference to push_back
. push_back
then constructs the new element from this temporary.
With // 3
you are just making the temporary construction that would otherwise happen implicitly explicit.
The second step above can be avoided completely by emplace_back
. Instead of taking a reference to an object of the target type, it takes arbitrary arguments by-reference and then constructs the new element directly from the arguments. No temporary std::string
is needed.
CodePudding user response:
Does 3 actually gives a benefit?
No. 1. already does a move without std::move
. 3. is just an unnecessarily explicit way to write 1.
2. is generally potentially most efficient. That's why your static analyser suggests it. But as the article explains, it's not significant in this case, and has potential compile time penalty. I believe you can avoid the potential compile cost using list.emplace_back( c_string_buffer);
, but that may be confusing to the reader.