0

How to understand the following std::string init syntax?

#include <iostream>
#include <string>

int main ()
{
    std::string y;
    std::string x = "x str";
    new (&y) std::string(x);
    std::cout << y << std::endl;

    return 0;
}

Output:

x str

Can we split the statement into 2 steps?

1.

string* temp = new std::string(x);

2.

(&y) = temp

So the original statement is just a shortcut for step 1 + 2.

Reference:

1. https://en.cppreference.com/w/cpp/string/basic_string/basic_string

2. https://en.cppreference.com/w/cpp/string/basic_string

skytree
  • 1,060
  • 2
  • 13
  • 38
  • 2
    There's nothing special here except the use of [placement new](https://en.cppreference.com/w/cpp/language/new#Placement_new), which isn't commonly used in code. – Alex Huszagh Aug 30 '19 at 21:01

1 Answers1

6

This is called "placement new". The basic idea is that you supply an address, and it invokes the constructor for that type to create an object at that address.

In a typical case, you'd give it an address that's "raw" memory though, not the address of an existing object. In fact, I'm not sure the code above really has defined behavior (though I'd have to look at the standard carefully to be sure whether it does or not).

It's most often used for things like collection classes, which allocate raw memory, the when you do something like insert or push_back, it constructs an object in that memory. The standard collections objects go through an allocator object to handle the construction, but in the end, it'll (at least usually) end up as placement new doing the real work.

The parameters you pass to new will be passed through to the constructor for the object being created. So in your case, it'll copy x into a new string at y's address (i.e., it'll replace y with a copy of x).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • What is the input and return value of the `new` function? For example `void* operator new(std::size_t, void*)`? The syntax is uncommon for me to understand. – skytree Aug 30 '19 at 21:08
  • I'm pretty sure the above example will cause it to fail to run the destructor of any data in the default constructed string, which is normally nullptr. Not great, but likely not catastrophic. – Alex Huszagh Aug 30 '19 at 21:13
  • @AlexanderHuszagh UB is still UB, which the code in the question invokes. – Fureeish Aug 30 '19 at 21:21
  • @Fureeish But it doesn't invoke UB, does it? Calling placement new on an object that was already initialized, although prone to resource leaks, as long as it is the same object as before, it well-defined, correct? https://stackoverflow.com/questions/27552466/c-is-constructing-object-twice-using-placement-new-undefined-behaviour – Alex Huszagh Aug 30 '19 at 21:33
  • It's bad code, don't get me wrong. I just don't believe it invokes UB. – Alex Huszagh Aug 30 '19 at 21:36
  • @Fureeish I sincerely doubt this is UB, unless you can prove that the program relies on side effects running from the destructor of `std::string` (which `basic_string` shouldn't be allowed to do): https://github.com/cplusplus/draft/pull/2342#issuecomment-428428936 – Alex Huszagh Aug 30 '19 at 21:50
  • 1
    @AlexanderHuszagh you are correct here, my apologies. – Fureeish Aug 30 '19 at 21:51
  • Reusing the storage of an object with a non-trivial destructor results in undefined behavior only if the application "depends on the side effects of the destructor". It's not really defined what that means though AFAIK. – Miles Budnek Aug 30 '19 at 21:51