Какую ошибку вызывает ошибка конструктора?
Я на Visual Studio 2017. Недавно, поскольку мне не нравились несоответствующие стандарты С++, я пошел дальше и отключил нестандартные расширения языка в параметрах. Все идет нормально. Теперь у меня проблема.
#include <iostream>
#include <vector>
struct Vertex
{
Vertex(float pos) { }
Vertex(Vertex& other) { }
};
std::vector<Vertex> arrayOfVertices;
int main()
{
arrayOfVertices.emplace_back(7.f);
}
Это не будет компилироваться в Visual Studio, единственная ошибка, которую он дает:
"в компиляторе произошла внутренняя ошибка"
Если я включаю языковые расширения, он компилируется отлично. Если я не буду убирать языковые расширения и сделать конструктор копии с помощью const Vertex&
, он компилируется отлично.
Итак, я попытался использовать GCC в некоторых онлайн-компиляторах, и если конструктор копирования не принимает аргумент ссылки на константу, он не будет компилироваться, предоставляя различные ошибки. Тот, который, казалось, имел наибольший смысл:
ошибка: недействительная инициализация неконстантной ссылки типа "Vertex & из rvalue типа" Vertex
Я думал, что конструкторы копирования не обязательно должны быть const, в моем случае я хотел бы что-то изменить в другой ссылке. Я знаю, что аргументы non-const не могут принимать ссылки r-value, но я тестировал их, и получается, что в vector::emplace_back()
конструктор копирования вообще не вызывается:
#include <iostream>
#include <vector>
struct Vertex
{
Vertex(float pos)
{
std::cout << "Calling constructor\n";
}
Vertex(const Vertex& other)
{
std::cout << "Calling copy constructor\n";
}
};
std::vector<Vertex> arrayOfVertices;
int main()
{
arrayOfVertices.emplace_back(7.f); // Normal constructor called if const,
// doesn't compile if non-const
auto buff = malloc(sizeof(Vertex)); // Placement new
new (buff) Vertex(7.f); // Normal constructor called whether const
// or non-const. This is what I thought emplace_back did
}
Поэтому я понятия не имею, что происходит. Сначала я хотел бы знать, почему это происходит, если конструктор копирования не вызывается, а также если есть способ взять неконтент в моем конструкторе копирования в этом случае, то есть с помощью vector::emplace_back()
, потому что он похоже, эта проблема возникает только при использовании vector::emplace_back()
.
Ответы
Ответ 1
Проблема заключается в том, что у вас нет конструктора перемещения.
Когда вы запрашиваете std::vector
to emplace_back
что-то, он должен убедиться, что для создания нового объекта у него достаточно памяти. Часть этой подпрограммы заключается в создании экземпляра кода, который перемещает элементы из старого буфера в любой выделенный выделенный буфер, если это необходимо. Этот код будет создан экземпляром шаблона, даже если во время выполнения не будет перераспределения.
У вашего класса есть пользовательский конструктор копирования, поэтому конструктор перемещения неявно удаляется. Таким образом, попытка переместить любой элемент в исходном буфере в новый, будет превращена в попытку скопировать с помощью разрешения перегрузки. Ваш фокус на размещении нового на самом деле является красной селедкой, реальная проблема очевидна в этом простом примере:
Vertex v1{7.f},
v2{std::move(v1)};
// Error, the xvalue from `move` can't bind to a non-const reference
Вы можете легко отключить ошибку, возвращая конструктор перемещения, например, явно по умолчанию:
struct Vertex
{
Vertex(float)
{
std::cout << "Calling constructor\n";
}
Vertex(Vertex&&) = default;
Vertex(Vertex&)
{
std::cout << "Calling copy constructor\n";
}
};
Никогда не забывайте, что в С++ 11 правилом 0/3 стало правило 0/3/5. Подумайте также о семантике перемещения для вас также.
Ответ 2
Очевидно, что это ошибка компилятора, если компилятор дает внутреннюю ошибку.
emplace_back(7.f)
использует конструктор Vertex(float pos)
для размещения объекта - конструктор-копия напрямую не участвует.
Фактическая причина ошибки отличается. Когда вы заменяете вектор, в общем случае может произойти перераспределение. Если это так, то все объекты в векторе должны быть перемещены в новое место в памяти.
Очевидно, что это условие выполнения, связанное с тем, происходит ли перераспределение. Невозможно выполнить ошибку компиляции во время выполнения; поэтому ошибка должна возникать при использовании времени emplace_back
во время компиляции, если объекты не поддерживают перераспределение; даже если вектор для этого вызова пуст.
Стандартная терминология содержится в С++ 14 Таблица 87: для того, чтобы вставить в вектор, тип элемента должен быть MoveInsertable и MoveAssignable.
Не вдаваясь в подробности, комбинация неконстантного конструктора-копии и никакого конструктора-указателя означает, что объект не выполняет требование MoveInsertable, так как аргумент rvalue в указанном требовании не будет связываться с не-const lvalue reference.