4

I'm pretty sure I've seen this asked on here before, but I can't seem to find the post when I tried searching, and I don't recall the answer.

Why is there no emplace or emplace_back for std::string?

I don't think it's related to the use of char, since you can have a vector of chars, and there's no problem with emplace_backing a char in the vector.

24n8
  • 1,898
  • 1
  • 12
  • 25
  • 2
    Some food for thought: `std::basic_string` can be used a generic container with some handy advantages over `std::vector`. See this CppCon talk: https://www.youtube.com/watch?v=SDJImePyftY – alter_igel Mar 04 '20 at 19:08
  • Writing and submitting a standards proposal for `std::basic_string` (and friends) to grow `emplace()`/`emplace_back()` is always an option. – Jesper Juhl Mar 04 '20 at 19:15
  • @alterigel The standard seems to allow only non-array POD types for `std::basic_string`, so the use as generic container is quite limited, see https://timsong-cpp.github.io/cppwp/n4659/strings#general-1 (and https://timsong-cpp.github.io/cppwp/n4659/strings#basic.string-1). – walnut Mar 05 '20 at 03:41

2 Answers2

8

There would be no gain over push_back

The power of emplace_back is that it constructs an object in-place, where it is designed to stay. No copies or moves are needed. And it also provides simpler syntax for creating objects, but that's just a minor advantage.

class Person
{
    std::string name;
    int age;
    double salary;
}

int main()
{
    std::vector<Person> people;
    people.push_back(Person{"Smith", 42, 10000.0}); //temporary object is needed, which is moved to storage
    people.emplace_back("Smith", 42, 10000.0); //no temporary object and concise syntax
}

For std::string (or any theoretical container that can only hold primitive types), the gain is nonexistent. No matter if you construct object in place or move it or copy it, you perform the same operation at assembly level - write some bytes at given memory.
Syntax also cannot become more concise than it is - there is no constructor that needs to be called. You can use a literal or another variable, but you have to do exactly same thing in both push_back and emplace_back

int main()
{
    std::vector<char> letters;
    letters.push_back('a');
    letters.emplace_back('a'); //no difference
}
Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
  • 3
    _"can only hold primiteve types"_ I don't think the standard forbids non-primitive types. cppreference says it has to be a trivial type, but [this](https://godbolt.org/z/DdSV_x) still compiles so I'm not sure what to think of this. I couldn't find a constraint in the standard other than _"char-like object"_ which isn't actually defined afaik. – Timo Mar 04 '20 at 19:10
  • 1
    It would still be nice for homogeneity. If you have a function template that works with containers generically, you either can't use `emplace_back` or you can't support `std::string`. – François Andrieux Mar 04 '20 at 19:11
  • `std::basic_string`, being a template, doesn't have to use `char`. – Jesper Juhl Mar 04 '20 at 19:11
  • Consider `std::basic_string` – Richard Critten Mar 04 '20 at 19:11
  • @RichardCritten That presumes you have some sort of `char_traits`. – François Andrieux Mar 04 '20 at 19:13
  • 2
    @François I think the needs of the many outweigh the needs of the few, here. When we add `emplace_back` for strings, many people would start to use it for imaginary gains and this would introduce confusion and ambiguity. For those few with the need for uniform container syntax, it would 1) not be hard to write a function emplaces if possible and push_backs if necessary and 2) this would probably already be necessary since `set` has `emplace` and not `emplace_back`. – n314159 Mar 04 '20 at 19:21
  • "There would be no gain over push_back" what is the gain `std::array::max_size()` over `std::array::size()`? – Slava Mar 04 '20 at 19:27
  • 2
    @n314159 actually according to https://en.cppreference.com/w/cpp/string/basic_string since C++17 `std::string` must satisfy SequenceContainer concept which requires `emplace_back` to exist so this looks like a defect to me – Slava Mar 04 '20 at 19:31
  • @n314159 There is already a solution to uniform insertion which is the `insert` member which all containers have (even maps and sets). And I don't agree that adding `emplace` members would lead to confusion. Maybe the performance gains are imaginary, but that's no different from any other container's `emplace_back` with certain types. There already exists a ton of `std::vector::emplace_back()` calls in countless projects and it causes no harm at all. While it's true that there isn't much to gain and it isn't free to add, introducing confusion is not a real cost in this case. – François Andrieux Mar 04 '20 at 19:34
  • 1
    @Slava It seems to me that the [standard](http://eel.is/c++draft/basic.string) only requires string to be a contiguous container which in turn only adds requirements to its iterators not its interface. – n314159 Mar 04 '20 at 19:38
  • @François I see your point, but I think, that often enough one would ask oneself "Why was emplace_back used here and not push_back?" as it is already the case for `vector`, when emplace is used willy-nilly. Of course there are arguments for both directions and I wouldn't complain if there were an `emplace_back` for string, but I also don't complain about its absence. – n314159 Mar 04 '20 at 19:42
  • @Slava [SequenceContainer](https://en.cppreference.com/w/cpp/named_req/SequenceContainer) only requires `emplace_back` for `deque`, `list` and `vector`. I think this is rather a defect in cppreference's wording – Timo Mar 04 '20 at 19:46
  • @Timo As n31419 pointed out `basic_string` is not a sequence container. cppreference says it is but that isn't supported by the standard. Edit : It looks like this might be a recent change. Edit 2 : Not it's not. – François Andrieux Mar 04 '20 at 19:52
  • Wouldn't an advantage be in templating? Say you have a templated function. Inside the function, there is a templated variable that can be `std::string` or `std::vector`, and you need to do some appending to the variable . For conformity, if they both had `emplace_back`, then you could operate as follows: `var.emplace_back(something)`, but since `std::string` doesn't have this functionality, this would only work if `var` was a `std::vector`. Edit: I just saw @François Andrieux comment above. I think his is identical to what I'm saying here. – 24n8 Mar 04 '20 at 20:25
  • 1
    `std::basic_string>` ... ow, I've hurt my brain. – Eljay Mar 04 '20 at 22:27
  • 1
    @Timo "*char-like*" is defined in https://timsong-cpp.github.io/cppwp/n4659/strings#general-1 to mean non-array POD, i.e. trivial and standard-layout. I think your example has undefined behavior. – walnut Mar 05 '20 at 03:36
  • Really good explanation. This is what I thought too before I saw your answer. Thank you! As a side note the primitive type are move-assignable/ constructible however there's no benefit of moving them. – Raindrop7 Oct 23 '21 at 21:33
0

The advantage of (e.g.) std::vector‹T>::emplace_back is that it constructs an object of type T directly in the vector, instead of constructing a temporary and then copying it in.

Since char is a primitive type, there is no advantage in one method over the other, so there is no reason for std::string::emplace_back to exist.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • 2
    There is a big reason for `std::string::emplace_back` to exist - common interface for generic programming – Slava Mar 04 '20 at 19:17