What does 'constructed in-place' mean?

This is primarily with regards to containers' emplace() method. I get that insert() copies the argument (i.e., invoking the argument type's copy constructor) and places the copied object into the container. emplace() doesn't invoke the copy constructor but beyond that it's not clear to me where the object constructed by emplace() reside (heap? stack? global?).

Lastly, with STL containers, are there really only 2 ways of putting elements in a container? Those being:

1. Copy the object (be it from heap or stack) and the *copy* is placed in the container.
2. Create the object in-place (I'm guessing it's stored on the heap).

Is there a way to pass ownership of the memory for the object to the container without having to create a container of pointers to the objects you want to store?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Foo
{
public:
    int foo;
    std::string foos;
    double food;

    // Constructor
    Foo(int f, std::string fs, double fd) : foo(f), foos(fs), food(fd) {}

    // Copy constructor
    Foo(const Foo& other)
    {
        std::cout << "Copy constructor invoked.\n";
        foo = other.foo;
        foos = other.foos;
        food = other.food;
    }
};

// Define < for usage with set
bool operator< (const Foo& lhs, const Foo& rhs) { return lhs.food < rhs.food; }

// Writes the member values of Foo and its address to os.
std::ostream& operator<< (std::ostream& os, const Foo& f)
{
    os << f.foo << "," << f.foos << "," << f.food << " Addr: " << "0x" << &f;
    return os;
}

int main()
{
    // Create stack member Foo f1 
    auto f1 = Foo(1, "one", 1.0);
    std::cout << "1. Addr f1: " << f1 << "\n";

    // Create heap member Foo f2
    Foo* f2 = new Foo(2, "two", 2.0);
    std::cout << "2. Addr f2: " << *f2 << "\n";

    std::set<Foo> fset;
    fset.insert(f1);
    fset.insert(*f2); // Heap allocated object also gets copied and the copy is put into the container.
    fset.emplace(3, "three", 3.0);

    for (auto &elm : fset)
    {
        std::cout << elm << "\n";
    }
}
Last edited on
> Is there a way to pass ownership of the memory for the object to the container
not understand what you mean

> where the object constructed by emplace() reside
it resides in the container, for example &v[42]
it is constructed in the place it will end in the container
Is there a way to pass ownership of the memory for the object to the container without having to create a container of pointers to the objects you want to store?

STL containers will move from rvalues:
1
2
3
std::string s = whatever();
std::vector<std::string> v;
v.push_back(std::move(s));


In order for objects in the container to be moved-from in all cases (e.g., when the vector resizes), those objects must provide non-throwing move operations. This restriction improves exception safety. Look up std::move_if_noexcept for more information. The noexcept keyword is easily overlooked, but move constructors, move assignments, and swap functions should always be non-throwing.

2 [...] (I'm guessing it's stored on the heap).

Not necessarily. Any source of storage is acceptable. If the object is sufficiently small, the storage that would otherwise represent a pointer, capacity, and size can be reinterpreted as the object. This is called the small buffer optimization, or SBO.

std::string is the usual example, but this optimization also occurs in other components - std::any, std::function, etc. Notably, std::vector is not a candidate for SBO.
Last edited on
ne555 wrote:
> Is there a way to pass ownership of the memory for the object to the container
not understand what you mean


I'm alluding to std::move() see [1]. I have a vague sense of what std::move() does, there are videos of people referring to it as a way to transfer "ownership of an object" [2], [3], but I'm not exactly sure how to write code to "move" an object into a container. [1] seems to suggest this is possible.

ne555 wrote:

it resides in the container, for example &v[42]
it is constructed in the place it will end in the container


AFAIK, the container allocates memory on the heap for objects that are put inside. So ... I'm going to conclude that "emplacing" an object in a container creates an object on the heap.


[1] SO: What is std:: move() and when should it be used?
https://stackoverflow.com/a/3413547/5972766
It's a new C++ way to avoid copies. For example, using a move constructor, a std::vector could just copy its internal pointer to data to the new object, leaving the moved object in an moved from state, therefore not copying all the data. This would be C++-valid.


[2] YT: CopperSpice: Smart Pointers
https://www.youtube.com/watch?v=kRVjG3qb7RE
@5:50 Ownership of an object can be transfered among unique_ptr using std::move()


[3] YT: CppCon 2019: Klaus Iglberger "Back to Basics: Move Semantics"
https://www.youtube.com/watch?v=St0MNEU5b0o
@16:36 Move doesn't do anything. It's a "transfer of ownership" (I presume to mean ownership of the responsibility to destroy the object)
Last edited on
mbozzi wrote:

STL containers will move from rvalues:
1
2
3
std::string s = whatever();
std::vector<std::string> v;
v.push_back(std::move(s));



Ah cool. I'm still a little bit shakey on move semantics so I don't see myself using v.push_back(std::move(obj)).

But I can imagine defining my own classes, creating objects (perhaps on the heap as on line 38 in my original code example), and wanting to put them in a container without wanting copies to be made and inserted in place of the actual object created.

This seems to be the job of emplace -- to create the object on the heap (or wherever, given what you said about SBO) for you.

Can I achieve the same end using:

v.push_back(std::move(s));

?
Last edited on
Ah cool. I'm still a little bit shakey on move semantics so I don't see myself using v.push_back(std::move(obj)).

That's okay. You get the benefits of move semantics for free when you write
v.push_back(f())
if f() returns by value, for example.

The operation Move performs the same function as Copy; except that the source of the move may be modified.

Can I achieve the same end using v.push_back(std::move(s))

No. push/insert always requires either a copy or move, whereas emplace requires neither a copy nor move in general. Consider
fset.emplace(3, "three", 3.0);
This doesn't copy or move any Foo object, because none exists until emplace constructs it directly within the set's data structure.
Contrast this with
fset.insert(Foo{3, "three", 3.0});
Since insert expects a reference to Foo as an argument, one must be created to initialize the reference parameter. Inside insert, the referent must be used to move-construct the Foo within the set's data structure.
Last edited on
Topic archived. No new replies allowed.