Некопируемые элементы в std :: vector из std :: lists

У меня есть не копируемый класс, упрощенный следующим образом:

struct NonCopyable
{
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator = (const NonCopyable&) = delete;
    NonCopyable(NonCopyable&&) = default;
    NonCopyable& operator = (NonCopyable&&) = default;
};

Я храню объекты этого класса в различных std :: list. В какой-то момент у меня есть пустой std :: vector этих списков, размер которого я хотел бы изменить, чтобы он содержал фиксированное количество списков пустой. Однако он жалуется на отсутствие конструктора копирования NonCopyable, хотя (насколько я понимаю) он не должен пытаться создать какие-либо NonCopyables!

std::vector<std::list<NonCopyable>> v; // OK
v.resize(4);                           // ERROR
v.emplace_back();                      // ERROR
std::list<NonCopyable> l;              // OK
v.push_back(l);                        // ERROR (unsurprisingly)
v.push_back(std::move(l));             // ERROR

Почему это? Есть ли способ сделать это, не делая класс копируемым?

Я использую VS2017 с / std: C++ 17 на случай, если это имеет значение.


Вот полный вывод ошибки при простой попытке изменить размер (первые две строки выше).

1>------ Build started: Project: testcore, Configuration: Debug x64 ------
1>testcard.cpp
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\xmemory0(881): error C2280: 'NonCopyable::NonCopyable(const NonCopyable &)': attempting to reference a deleted function
1>[...]\testcard.cpp(30): note: see declaration of 'NonCopyable::NonCopyable'
1>[...]\testcard.cpp(30): note: 'NonCopyable::NonCopyable(const NonCopyable &)': function was explicitly deleted
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(711): note: see reference to function template instantiation 'void std::_Default_allocator_traits<_Alloc>::construct<_Ty,const NonCopyable&>(_Alloc &,_Objty *const ,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Alloc=std::allocator<std::_List_node<NonCopyable,std::_Default_allocator_traits<std::allocator<NonCopyable>>::void_pointer>>,
1>            _Ty=NonCopyable,
1>            _Objty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(716): note: see reference to function template instantiation 'void std::_Default_allocator_traits<_Alloc>::construct<_Ty,const NonCopyable&>(_Alloc &,_Objty *const ,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Alloc=std::allocator<std::_List_node<NonCopyable,std::_Default_allocator_traits<std::allocator<NonCopyable>>::void_pointer>>,
1>            _Ty=NonCopyable,
1>            _Objty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(947): note: see reference to function template instantiation 'std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_List_buy<_Ty,_Alloc>::_Buynode<const NonCopyable&>(std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *,std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Alloc=std::allocator<NonCopyable>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(950): note: see reference to function template instantiation 'std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_List_buy<_Ty,_Alloc>::_Buynode<const NonCopyable&>(std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *,std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Alloc=std::allocator<NonCopyable>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(1308): note: see reference to function template instantiation 'void std::list<NonCopyable,std::allocator<_Ty>>::_Insert<const NonCopyable&>(std::_List_unchecked_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>,std::_Iterator_base0>,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(1308): note: see reference to function template instantiation 'void std::list<NonCopyable,std::allocator<_Ty>>::_Insert<const NonCopyable&>(std::_List_unchecked_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>,std::_Iterator_base0>,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(1265): note: see reference to function template instantiation 'void std::list<NonCopyable,std::allocator<_Ty>>::_Insert_range<_Iter>(std::_List_unchecked_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>,std::_Iterator_base0>,_Iter,_Iter,std::forward_iterator_tag)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Iter=std::_List_const_iterator<std::_List_val<std::_List_simple_types<NonCopyable>>>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(1264): note: see reference to function template instantiation 'void std::list<NonCopyable,std::allocator<_Ty>>::_Insert_range<_Iter>(std::_List_unchecked_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>,std::_Iterator_base0>,_Iter,_Iter,std::forward_iterator_tag)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Iter=std::_List_const_iterator<std::_List_val<std::_List_simple_types<NonCopyable>>>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(800): note: see reference to function template instantiation 'std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>> std::list<_Ty,std::allocator<_Ty>>::insert<std::_List_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,void>(std::_List_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,_Iter,_Iter)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Iter=std::_List_const_iterator<std::_List_val<std::_List_simple_types<NonCopyable>>>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(800): note: see reference to function template instantiation 'std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>> std::list<_Ty,std::allocator<_Ty>>::insert<std::_List_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,void>(std::_List_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,_Iter,_Iter)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Iter=std::_List_const_iterator<std::_List_val<std::_List_simple_types<NonCopyable>>>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(796): note: while compiling class template member function 'std::list<NonCopyable,std::allocator<_Ty>>::list(const std::list<_Ty,std::allocator<_Ty>> &)'
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\xmemory0(881): note: see reference to function template instantiation 'std::list<NonCopyable,std::allocator<_Ty>>::list(const std::list<_Ty,std::allocator<_Ty>> &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\vector(1812): note: see reference to class template instantiation 'std::list<NonCopyable,std::allocator<_Ty>>' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\vector(1811): note: while compiling class template member function 'std::list<NonCopyable,std::allocator<_Ty>> *std::vector<std::list<_Ty,std::allocator<_Ty>>,std::allocator<std::list<_Ty,std::allocator<_Ty>>>>::_Udefault(std::list<_Ty,std::allocator<_Ty>> *,const unsigned __int64)'
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\vector(1479): note: see reference to function template instantiation 'std::list<NonCopyable,std::allocator<_Ty>> *std::vector<std::list<_Ty,std::allocator<_Ty>>,std::allocator<std::list<_Ty,std::allocator<_Ty>>>>::_Udefault(std::list<_Ty,std::allocator<_Ty>> *,const unsigned __int64)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>[...]\testcard.cpp(37): note: see reference to class template instantiation 'std::vector<std::list<NonCopyable,std::allocator<_Ty>>,std::allocator<std::list<_Ty,std::allocator<_Ty>>>>' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>Done building project "testcore.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 2 up-to-date, 0 skipped ==========
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
744
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Как отметил @ j6t, кажется, что это невозможно. Вектор с std::list с некопируемыми элементами не может быть расширен по стандарту (...). Если бы это был мой частный проект, я бы на самом деле рассмотрел что-то вроде этого:

#include <iostream>
#include <vector>
#include <list>

namespace xxx {
    // <flame_bait>
    template<class T, class Enable = void>
    struct list : std::list<T> {
        list() : std::list<T>() {}
        list(list&&) = default;
        list& operator=(list&&) = default;
        list(const list&) = delete;
        list& operator=(const list&) = delete;
    };

    template<class T>
    struct list<T, typename std::enable_if<std::is_copy_constructible<T>::value>::type> : std::list<T> {};
    // </flame_bait>
}

struct NonCopyable
{
    int m_id;
    NonCopyable(int id = 0) : m_id(id) {}
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator = (const NonCopyable&) = delete;
    NonCopyable(NonCopyable&&) noexcept = default;
    NonCopyable& operator = (NonCopyable&&) noexcept = default;
    ~NonCopyable() = default;
    friend std::ostream& operator<<(std::ostream&, const NonCopyable&);
};

std::ostream& operator<<(std::ostream& os, const NonCopyable& nc) {
    os << "I am not a free man, I am number " << nc.m_id;
    return os;
}

int main() {
    std::vector<xxx::list<NonCopyable>> v;
    v.resize(4);                    // now working 

    int id = 0;
    for (auto& l : v) {
        l.emplace_back(++id);       // emplace_back one NonCopyable per list  
    }

    xxx::list<NonCopyable> li;      // Create a separate list
    li.emplace_back(++id);          // create one...
    v.emplace_back(std::move(li));  // and move list to vector is now working

    for (auto& l : v) {
        for (auto& nc : l) {
            std::cout << nc << "\n";
        }
    }
}

Ожидаемый результат:

I am not a free man, I am number 1
I am not a free man, I am number 2
I am not a free man, I am number 3
I am not a free man, I am number 4
I am not a free man, I am number 5

Кажется, я пропустил ту часть, где v.resize(4); не работает. Что за сообщение об ошибке в этой строке?

Ted Lyngmo 22.11.2018 14:59
error C2280: 'NonCopyable::NonCopyable(const NonCopyable &)': attempting to reference a deleted function. Я добавил в сообщение полный вывод об ошибке.
Uri Granta 22.11.2018 15:11

Здорово! Как ни странно ... :-) Я попробую в VS, когда вернусь домой.

Ted Lyngmo 22.11.2018 15:14

В довершение всего: v.reserve(4); выдает ту же ошибку.

Ted Lyngmo 22.11.2018 16:20

Я добавил xxx::list, заменяющий std::list, который, надеюсь, должен работать как контейнер как для копируемых, так и для некопируемых объектов. Я плохо разбираюсь в шаблонах, так что имейте в виду ...

Ted Lyngmo 22.11.2018 18:47
Ответ принят как подходящий

std::list - это не noexcept-подвижный, но его можно копировать. Следовательно, std::vector в любом случае предпочитает конструктор копирования list своему конструктору перемещения.

Чтобы обойти эту проблему, вы должны поместить свой std::list в класс, который нельзя копировать.

Спасибо. Что, если класс NonCopyable также не был перемещаем? (Я считаю, что перемещение объектов между списками все еще возможно, поскольку это не делает ссылки недействительными)

Uri Granta 22.11.2018 16:02

@UriZarfaty Можно было бы перемещать узлы списка с помощью splice, но std::vector не знает об этом.

j6t 22.11.2018 16:04

Так что это про QoI. Неудивительно, что Clang и GCC его принимают. github.com/llvm-mirror/libcxx/blob/master/include/list#L1315

StoryTeller - Unslander Monica 22.11.2018 16:09

@StoryTeller Это не совсем QoI, это вопрос буквального следования Стандарту. Я не вижу спецификации noexcept для конструктора перемещения в синопсисе списка. Тем не менее, я согласен с тем, что поведение Clang и GCC предпочтительнее.

j6t 22.11.2018 16:42

@ j6t - Стандарт тоже имеет спорное значение QoI. Что ж, точнее было бы качество спецификации, но суть все равно остается в силе.

StoryTeller - Unslander Monica 22.11.2018 16:58

list от MSVC использует динамически выделяемый контрольный узел, поэтому конструктор перемещения не может быть noexcept. (Компромисс в том, что такие вещи, как swap, не делают недействительными конечные итераторы.)

T.C. 22.11.2018 18:12

Действительно ли std::list<T> должен быть копируемым, если T не является Cpp17CopyInsertable? Я раньше не читал стандарты и у меня небольшие проблемы с пониманием container.requirements.

Ted Lyngmo 23.11.2018 08:05

@TedLyngmo Невозможно, чтобы list<T> был копируемым, если T не копируемый. Дело в том, что vector<T> очень параноик и не использует конструкцию перемещения T, если такая конструкция перемещения может вызывать исключения.

j6t 23.11.2018 08:24

Хорошо, но если std::list<T> действительно нельзя копировать, а T - нет, я действительно не понимаю, как мой хак (xxx::list ниже) может заставить его работать? Единственное, что я сделал, это скопировал delete, если T не копируется, а затем vector использовал конструкцию перемещения.

Ted Lyngmo 23.11.2018 08:38

@TedLyngmo Причина, по которой он компилируется, заключается в том, что, когда T не копируется, но все еще может быть сконструирован для перемещения (например, ваш xxx::list), тогда vector<T> должен использовать конструктор перемещения T, даже с риском того, что он вызовет исключение, потому что нет более безопасного выбора .

j6t 23.11.2018 09:25

@ j6t да, но почему std::is_copy_constructible<std::list<NonCopyable>>::value верен (даже в gcc / clang)?

Ted Lyngmo 23.11.2018 09:37

@TedLyngmo Поскольку нигде в спецификации std::list<T> не сказано, что «копирующий конструктор исключается из набора перегрузки, если T не может быть скопирован». Следовательно, конструктор копирования std::list всегда доступен, и это все, на что обращает внимание std::is_copy_constructible.

j6t 23.11.2018 09:42

Хорошо, вот где у меня проблемы с пониманием того, как читать стандартные шоу. X(a) и X u(a); оба Requires: T is Cpp17CopyInsertable into X. Я интерпретировал это как то, что они не должны быть доступны в этом случае.

Ted Lyngmo 23.11.2018 09:53

Эти предложения Requires являются директивами для программиста, а не для компилятора. Если условия нарушаются, вы получаете по заслугам; в лучшем случае он не компилируется, в худшем - это неопределенное поведение.

j6t 23.11.2018 11:13

Другие вопросы по теме