Home > front end >  What is the efficient way to add a std::string to a STL container with a value of a c-str buffer in
What is the efficient way to add a std::string to a STL container with a value of a c-str buffer in

Time:03-03

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.

  • Related