Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default constructor prevents from calling emplace_back

Tags:

It seems that adding a default constructor prevents from calling emplace_back and produces the error message: "static assertion failed: type is not assignable" (gcc 5.3 with -std=c++14). Here is a simple code that illustrates the issue:

class A {
public:
    int a;
    A() = default;
    A(int a) {
        this->a = a;
    }
    A(A const & a) = delete;
    A& operator =(A const & a) = delete;
    A(A && a) = default;
    A& operator =(A && a) = default;
};

int main() {

    A a(4);
    std::vector<A> vec;
    vec.emplace_back(std::move(a)); // Error: type is not assignable
    return 0;
}

When removing the default constructor, the error goes away! Also, if the default constructor is defined (even if it does nothing), the error also goes away:

class A {
public:
    int a;
    A() {
    }
    A(int a) {
        this->a = a;
    }
    A(A const & a) = delete;
    A& operator =(A const & a) = delete;
    A(A && a) = default;
    A& operator =(A && a) = default;
};

int main() {

    A b;
    A a(4);
    std::vector<A> vec;
    vec.emplace_back(std::move(a)); // Error gone
    return 0;
}

It seems that "A() = default;" is what is causing the problem. Is this normal behaviour on part of the compiler or is it a bug?

like image 297
S. K. Avatar asked Jan 25 '16 21:01

S. K.


People also ask

Does Emplace_back call constructor?

So you can emplace_back does use the desired constructor to create the element and call copy constructor when it need to grow the storage. You can call reserve with enough capacity upfront to avoid the need to call copy constructor.

What does Emplace_back mean in C++?

Description. The C++ function std::vector::emplace_back() inserts new element at the end of vector. Reallocation happens if there is need of more space. This method increases container size by one.

Should I use emplace_ back or push_ back?

You should definitely use emplace_back when you need its particular set of skills — for example, emplace_back is your only option when dealing with a deque<mutex> or other non-movable type — but push_back is the appropriate default. One reason is that emplace_back is more work for the compiler.


1 Answers

It's a libstdc++ bug (edit: reported as bug 69478).

Briefly, libstdc++'s std::vector, as relevant here, uses std::uninitialized_copy (paired with move iterators) to move elements on reallocation, which is reduced to std::copy if the type is trivial and the iterators' reference types are assignable (i.e., the assignment operator that would conceptually be used is usable).

Then, the std::copy for pointers to trivial types (or in our case, a move_iterator wrapping a pointer) is in turn optimized into a call to memmove coupled with a check for is_copy_assignable. Of course, that check is wrong in this case, since the uninitialized_copy, paired with move iterators, only requires the thing to be move constructible.

When you don't have a default constructor or if the default constructor is user-defined, then the class isn't trivial, so you don't hit the code path that triggers this bug.

like image 156
T.C. Avatar answered Oct 03 '22 18:10

T.C.