Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emplacing in vector using default constructor

I want to use vector::emplace to default construct a non-copyable and non-assignable object and then use specific methods on the object using an iterator to the newly created object. Note that there are no parameterized constructors of the class just the default constructor. A simple example is:

#include <iostream>
#include <vector>
using namespace std;

class Test {
public:
    Test() {}
private:
    Test(const Test&) = delete;             // To make clas un-copyable.
    Test& operator=(const Test&) = delete;

    int a_;
};

int main() {
    vector<Test> test_vec;
    test_vec.emplace_back();     // <---- fails

    return 0;
}

vector::emplace() constructs a new object but requires arguments to a non-default constructor. vector::emplace_back() will construct at the end of the vector.

Is there a way to emplace with default construction. Is there a way to use piecewise construction or default forwarding perhaps using std::piecewise_construct as there is for maps? For example, in the case of maps, we can use:

std::map<int,Test> obj_map;
int val = 10;
obj_map.emplace(std::piecewise_construct,
                std::forward_as_tuple(val),
                std::forward_as_tuple());

Is there something similar for vectors?

like image 427
Chizzy C Avatar asked Nov 21 '14 18:11

Chizzy C


3 Answers

As pointed out by @dyp and @Casey in the comments, std::emplace will not work for vectors of the Test class as the class is neither movable nor copyable because "the user-declared copy constructor suppresses generation of the default move constructor" (@Casey).

To use emplace here, the class will need to be movable or copyable. We can make the class movable by explicitly defining (and defaulting) the move constructors:

public:
    Test(Test&& other) = default;
    Test& operator=(Test&& other) = default;

This will also implicitly make the class not-copyable "since declaring the move operations will suppress implicit generation of the copies." (@Casey)

Now we can use std::emplace_back() and then use vector::back() to call methods of the newly created object.

like image 105
Chizzy C Avatar answered Oct 16 '22 19:10

Chizzy C


For map, easy:

std::map<int, Object> obj_map;
obj_map[10]; // default-constructs an object with key 10

Otherwise, what you have works too:

obj_map.emplace(std::piecewise_construct,
                std::forward_as_tuple(10),
                std::forward_as_tuple(args, to, construct, with));

[edit] The equivalent for vector is emplace_back:

obj_vector.emplace_back(); // default construct
obj_vector.emplace_back(args, to, std::move(construct), with); // forward these
like image 35
Barry Avatar answered Oct 16 '22 17:10

Barry


vector::emplace_back() will construct at the end of the vector but also require arguments.

Parameter packs can be empty. Thus the variadic template emplace_back can be called without arguments; I.e.

vector<VeryLimitedClass> vec;
vec.emplace_back();

Is valid code that initializes an object of type VeryLimitedClass through its default constructor and "emplaces" it at the back of vec.

like image 38
Columbo Avatar answered Oct 16 '22 19:10

Columbo