Хранение не копируемого объекта без конструктора по умолчанию на карте (C ++ 11)

Я пытаюсь реализовать класс, который представляет соединение с файлом, следовательно, это должен быть не копируемый класс. Кроме того, поскольку для создания объекта требуется имя файла, я бы хотел удалить конструктор по умолчанию.

Вот упрощенное определение такого класса:

class Elem
{
public:
Elem() = delete;
Elem(string name) : file(new ifstream(name,ios::in)) {}

Elem(const Elem&) = delete;
Elem& operator=(const Elem& o) = delete;

Elem& operator=(Elem && o)
{
swap(o.file,file);
o.file = nullptr;
}
Elem(Elem &&o) : file(nullptr)
{
swap(file,o.file);
}

~Elem()
{
if(file!=nullptr){
file->close();
delete file;
}
}
protected:
ifstream* file;
};

Однако, когда я пытаюсь сохранить такие объекты в стандартной карте, код не компилируется:

int main(){
map<string,Elem> m;
m["file1"] = move(Elem("file1"));
cout<<m.size()<<endl;
return 0;
}

Я получаю следующие ошибки (gcc 4.9.1):

In file included from /usr/include/c++/4.9.1/bits/stl_map.h:63:0,
from /usr/include/c++/4.9.1/map:61,
from test.cpp:1:
/usr/include/c++/4.9.1/tuple: In instantiation of ‘std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >&&}; unsigned int ..._Indexes1 = {0u}; _Args2 = {}; unsigned int ..._Indexes2 = {}; _T1 = const std::basic_string<char>; _T2 = Elem]’
/usr/include/c++/4.9.1/tuple:1088:63:   required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >&&}; _Args2 = {}; _T1 = const std::basic_string<char>; _T2 = Elem]’
/usr/include/c++/4.9.1/ext/new_allocator.h:120:4:   required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const std::basic_string<char>, Elem>; _Args = {const std::piecewise_construct_t&, std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _Tp = std::_Rb_tree_node<std::pair<const std::basic_string<char>, Elem> >]’
/usr/include/c++/4.9.1/bits/alloc_traits.h:253:4:   required from ‘static std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::pair<const std::basic_string<char>, Elem>; _Args = {const std::piecewise_construct_t&, std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _Alloc = std::allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char>, Elem> > >; std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> = void]’
/usr/include/c++/4.9.1/bits/alloc_traits.h:399:57:   required from ‘static decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::pair<const std::basic_string<char>, Elem>; _Args = {const std::piecewise_construct_t&, std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _Alloc = std::allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char>, Elem> > >; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]’
/usr/include/c++/4.9.1/bits/stl_tree.h:423:42:   required from ‘std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_create_node(_Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _Key = std::basic_string<char>; _Val = std::pair<const std::basic_string<char>, Elem>; _KeyOfValue = std::_Select1st<std::pair<const std::basic_string<char>, Elem> >; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, Elem> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<std::pair<const std::basic_string<char>, Elem> >*]’
/usr/include/c++/4.9.1/bits/stl_tree.h:1790:64:   required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_emplace_hint_unique(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator, _Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >&&>, std::tuple<>}; _Key = std::basic_string<char>; _Val = std::pair<const std::basic_string<char>, Elem>; _KeyOfValue = std::_Select1st<std::pair<const std::basic_string<char>, Elem> >; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, Elem> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, Elem> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator = std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char>, Elem> >]’
/usr/include/c++/4.9.1/bits/stl_map.h:519:8:   required from ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = std::basic_string<char>; _Tp = Elem; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, Elem> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = Elem; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = std::basic_string<char>]’
test.cpp:41:14:   required from here
/usr/include/c++/4.9.1/tuple:1099:70: erreur: use of deleted function ‘Elem::Elem()’
second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
^
test.cpp:11:5: note: declared here
Elem() = delete;

Любые подсказки о том, как этого можно достичь? Их другие недостатки дизайна в моей структуре?

заранее спасибо

4

Решение

Что вы делаете, это:

map<string,Elem> m;
m["file1"] =              // this default-constructs an Elem
// and returns a reference to it in the correct spot
move(Elem("file1"));  // and THEN move-assigns it

Что тебе необходимо сделать:

map<string, Elem> m;
m.emplace("file1", Elem("file1"));

или же

m.insert(std::make_pair("file1", Elem("file1"));

или же

m.emplace(std::piecewise_construct,
std::forward_as_tuple("file1"),
std::forward_as_tuple("file1"));

Что касается вашего дизайна, то в комментариях указывается на проблему, из-за которой вы, возможно, пропускаете поток. Но это на самом деле очень легко исправить. То, что вы выражаете, — это уникальное право собственности на файл, на которое оно может быть передано. Есть тип для этого: unique_ptr!

class Elem {
std::unique_ptr<ifstream> file;

public:
Elem() = delete;
Elem(string name) : file(new ifstream(name,ios::in)) {}

Elem(Elem&& ) = default;
Elem& operator=(Elem&& ) = default;
~Elem() = default;

Elem(const Elem&) = delete;
Elem& operator=(const Elem&) = delete;
};

С помощью unique_ptr делает те default/delete линии даже не нужны (кроме конструктора по умолчанию — поскольку это обязательное требование), что даже лучше (см. «Правило нуля»). Я просто иллюстрирую их для ясности.

3

Другие решения


По вопросам рекламы [email protected]