2014-11-22 9 views
4

Я пытаюсь реализовать класс, представляющий соединение с файлом, следовательно, он должен быть классом, не подлежащим копированию. Кроме того, поскольку для создания объекта требуется имя файла, я хотел бы удалить конструктор по умолчанию.Хранение не копируемого объекта без конструктора по умолчанию на карте (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; 

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

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

+1

'operator []' требует конструктивности по умолчанию: если ключ отсутствует, создается узел, где значение инициализируется значением (вызывает значение по умолчанию ctor). 'operator []' должен возвращать ссылку на действительный объект, поэтому он либо должен использовать существующий, либо создать его. Вместо этого используйте 'lower_bound',' find' и 'insert'. – dyp

+0

@ dyp: 'insert'? Больше похоже на 'emplace'. – Deduplicator

+0

@Deduplicator 'insert' должен иметь перегрузку, но да,' emplace' также должен быть в этом списке. («' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'Иногда бывает быстрее, чем' emplace', но не уверен в '' карте', хотя.) – dyp

ответ

3

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

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 линии даже не надо (кроме конструктора по умолчанию один - так как это требование вы исполнение), что даже лучше (см Правило ноль). Я просто иллюстрирую их для ясности.

+0

Еще движение-ctor или copy-ctor.Почему бы не дать сам ctor-аргумент? – Deduplicator

+0

@Deduplicator вам нужно 'move (Elem (" file1 "))', чтобы сделать работу вставки? Я думал, что есть способ по-прежнему использовать 'insert' для noncopyable типов. – Barry

+0

- тип подвижный? – Deduplicator