存储地图中没有默认构造函数的非可复制对象(C ++ 11)

时间:2014-11-22 23:17:12

标签: c++ c++11 map move-semantics noncopyable

我正在尝试实现一个表示文件连接的类,因此它应该是一个不可复制的类。此外,由于创建对象需要文件名,我想删除默认构造函数。

以下是此类的简化定义:

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 个答案:

答案 0 :(得分: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行甚至不需要(默认构造函数除外 - 因为这是您要求执行的要求),这是甚至更好(见零规则)。我只是为了清晰起见而对它们进行了说明。