Если я пропущу следующий код через мой снимок GCC 4.7, он попытается скопировать unique_ptr
s в вектор.
#include <vector>
#include <memory>
int main() {
using move_only = std::unique_ptr<int>;
std::vector<move_only> v { move_only(), move_only(), move_only() };
}
Очевидно, это не может работать, потому что std::unique_ptr
не копируется:
ошибка: использование удаленной функции 'std :: unique_ptr <_Tp, _Dp> :: unique_ptr (const std :: unique_ptr <_Tp, _Dp> &) [с _Tp = int; _Dp = std :: default_delete; std :: unique_ptr <_Tp, _Dp> = std :: unique_ptr] '
Правильно ли GCC пытается скопировать указатели из списка инициализаторов?
c++
c++11
initializer-list
move-semantics
Р. Мартиньо Фернандес
источник
источник
Ответы:
Синопсис
<initializer_list>
в 18.9 достаточно ясно показывает, что элементы списка инициализаторов всегда передаются через константную ссылку. К сожалению, в текущей версии языка не существует способа использования семантики перемещения в элементах списка инициализаторов.В частности, у нас есть:
источник
const
, которые нельзя отбросить в правильно сформированной программе.Изменить: поскольку @Johannes, похоже, не хочет публиковать лучшее решение в качестве ответа, я просто сделаю это.
Итераторы, возвращаемые с помощью,
std::make_move_iterator
будут перемещать указанный элемент при разыменовании.Исходный ответ: мы собираемся использовать здесь небольшой вспомогательный тип:
К сожалению, простой код здесь не работает:
Поскольку стандарт по какой-либо причине не определяет конструктор конвертирующей копии, подобный этому:
initializer_list<rref_wrapper<move_only>>
Созданный брекет-Init-лист ({...}
) не будет конвертировать вinitializer_list<move_only>
том , чтоvector<move_only>
происходит. Итак, здесь нам нужна двухэтапная инициализация:источник
std::ref
, non? Может, это стоит назватьstd::rref
.move_only m[] = { move_only(), move_only(), move_only() }; std::vector<move_only> v(std::make_move_iterator(m), std::make_move_iterator(m + 3));
.move_iterator
.std::distance
для итераторов прямого или лучшего качества иstd::move_iterator
адаптирует категорию базового итератора. В любом случае, хорошее и лаконичное решение. Может, выложить как ответ?Как упоминалось в других ответах, поведение
std::initializer_list
заключается в том, чтобы удерживать объекты по значению и не позволять перемещаться, поэтому это невозможно. Вот один из возможных обходных путей, использующий вызов функции, в которой инициализаторы задаются как переменные аргументы:К сожалению, это
multi_emplace(foos, {});
не удается, так как он не может определить тип для{}
, поэтому для объектов, которые будут построены по умолчанию, вы должны повторить имя класса. (или использоватьvector::resize
)источник
Используя уловку Йоханнеса Шауба с
std::make_move_iterator()
withstd::experimental::make_array()
, вы можете использовать вспомогательную функцию:Смотрите вживую Coliru.
Возможно, кто-то может использовать
std::make_array()
уловку, чтобы позволитьmake_vector()
делать свое дело напрямую, но я не видел, как это сделать (точнее, я пробовал то, что, по моему мнению, должно работать, потерпел неудачу и продолжил). В любом случае компилятор должен иметь возможность встроить массив в векторное преобразование, как это делает Clang с включенным O2 GodBolt.источник
Как было указано, невозможно инициализировать вектор типа «только перемещение» с помощью списка инициализаторов. Решение, изначально предложенное @Johannes, работает нормально, но у меня есть другая идея ... Что, если мы не создадим временный массив, а затем переместим элементы оттуда в вектор, а будем использовать размещение
new
для инициализации этого массива уже вместо блок памяти вектора?Вот моя функция для инициализации вектора
unique_ptr
с использованием пакета аргументов:источник
result.data()
не является указателем на какую-то случайную память. Это указатель на объект . Подумайте, что происходит с этим плохим объектом, когда вы кладете на него новый.