Конструктор по умолчанию предотвращает вызов emplace_back

Похоже, что добавление конструктора по умолчанию предотвращает вызов emplace_back и выдает сообщение об ошибке: "static assertion failed: type не присваивается" (gcc 5.3 с -std = С++ 14). Вот простой код, который иллюстрирует проблему:

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;
}

При удалении конструктора по умолчанию ошибка исчезает! Кроме того, если конструктор по умолчанию определен (даже если он ничего не делает), ошибка также исчезает:

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;
}

Кажется, что "A() = default;" что вызывает проблему. Это нормальное поведение на части компилятора или это ошибка?

Ответы

Ответ 1

Это ошибка libstdС++ (изменение: сообщено как ошибка 69478).

Вкратце, libstdС++ std::vector, как уместно здесь, использует std::uninitialized_copy (в паре с итераторами перемещения) для перемещения элементов при перераспределении, что сводится к std::copy, если тип тривиален и ссылочные типы итераторов назначаются (т.е. можно использовать оператор присваивания, который был бы концептуально использован).

Затем std::copy для указателей на тривиальные типы (или в нашем случае, a move_iterator обертывание указателя) в свою очередь оптимизируется на вызов memmove в сочетании с проверкой на is_copy_assignable. Конечно, эта проверка неверна в этом случае, так как uninitialized_copy, соединенный с итераторами перемещения, требует только того, чтобы вещь была конструктивной.

Если у вас нет конструктора по умолчанию или конструктор по умолчанию определяется пользователем, то класс не является тривиальным, поэтому вы не попадаете в путь кода, который запускает эту ошибку.