为什么我不能std ::在std :: sets之间移动std :: unique_ptrs?

时间:2016-07-30 22:36:16

标签: c++ c++11 std move-semantics unique-ptr

我真的想将unique_ptr从一个std::set移到另一个#include <memory> #include <algorithm> #include <set> int main() { std::set<std::unique_ptr<int>> a; std::set<std::unique_ptr<int>> b; a.insert({0, std::unique_ptr<int>(new int(42))}); std::move(a.begin(), a.end(), std::inserter(b, b.end())); }

[root@localhost ~]# g++ test.cpp -std=c++11 -o test
In file included from /usr/include/c++/4.8.2/set:60:0,
                 from test.cpp:2:
/usr/include/c++/4.8.2/bits/stl_tree.h: In instantiation of ‘std::_Rb_tree_node<_Val>::_Rb_tree_node(_Args&& ...) [with _Args = {const std::unique_ptr<int, std::default_delete<int> >&}; _Val = std::unique_ptr<int>]’:
/usr/include/c++/4.8.2/ext/new_allocator.h:120:4:   required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::_Rb_tree_node<std::unique_ptr<int> >; _Args = {const std::unique_ptr<int, std::default_delete<int> >&}; _Tp = std::_Rb_tree_node<std::unique_ptr<int> >]’
/usr/include/c++/4.8.2/bits/alloc_traits.h:254:4:   required from ‘static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::_Rb_tree_node<std::unique_ptr<int> >; _Args = {const std::unique_ptr<int, std::default_delete<int> >&}; _Alloc = std::allocator<std::_Rb_tree_node<std::unique_ptr<int> > >; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]’
/usr/include/c++/4.8.2/bits/alloc_traits.h:393: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::_Rb_tree_node<std::unique_ptr<int> >; _Args = {const std::unique_ptr<int, std::default_delete<int> >&}; _Alloc = std::allocator<std::_Rb_tree_node<std::unique_ptr<int> > >; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]’
/usr/include/c++/4.8.2/bits/stl_tree.h:408:36:   required from ‘std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_create_node(_Args&& ...) [with _Args = {const std::unique_ptr<int, std::default_delete<int> >&}; _Key = std::unique_ptr<int>; _Val = std::unique_ptr<int>; _KeyOfValue = std::_Identity<std::unique_ptr<int> >; _Compare = std::less<std::unique_ptr<int> >; _Alloc = std::allocator<std::unique_ptr<int> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<std::unique_ptr<int> >*]’
/usr/include/c++/4.8.2/bits/stl_tree.h:1023:66:   required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr, std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr, _Arg&&) [with _Arg = const std::unique_ptr<int>&; _Key = std::unique_ptr<int>; _Val = std::unique_ptr<int>; _KeyOfValue = std::_Identity<std::unique_ptr<int> >; _Compare = std::less<std::unique_ptr<int> >; _Alloc = std::allocator<std::unique_ptr<int> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::unique_ptr<int> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr = std::_Rb_tree_node_base*]’
/usr/include/c++/4.8.2/bits/stl_tree.h:1482:33:   required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique_(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator, _Arg&&) [with _Arg = const std::unique_ptr<int>&; _Key = std::unique_ptr<int>; _Val = std::unique_ptr<int>; _KeyOfValue = std::_Identity<std::unique_ptr<int> >; _Compare = std::less<std::unique_ptr<int> >; _Alloc = std::allocator<std::unique_ptr<int> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::unique_ptr<int> >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator = std::_Rb_tree_const_iterator<std::unique_ptr<int> >]’
/usr/include/c++/4.8.2/bits/stl_tree.h:1722:37:   required from ‘void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(_II, _II) [with _InputIterator = const std::unique_ptr<int>*; _Key = std::unique_ptr<int>; _Val = std::unique_ptr<int>; _KeyOfValue = std::_Identity<std::unique_ptr<int> >; _Compare = std::less<std::unique_ptr<int> >; _Alloc = std::allocator<std::unique_ptr<int> >]’
/usr/include/c++/4.8.2/bits/stl_set.h:518:4:   required from ‘void std::set<_Key, _Compare, _Alloc>::insert(_InputIterator, _InputIterator) [with _InputIterator = const std::unique_ptr<int>*; _Key = std::unique_ptr<int>; _Compare = std::less<std::unique_ptr<int> >; _Alloc = std::allocator<std::unique_ptr<int> >]’
/usr/include/c++/4.8.2/bits/stl_set.h:530:9:   required from ‘void std::set<_Key, _Compare, _Alloc>::insert(std::initializer_list<_Tp>) [with _Key = std::unique_ptr<int>; _Compare = std::less<std::unique_ptr<int> >; _Alloc = std::allocator<std::unique_ptr<int> >]’
test.cpp:9:49:   required from here
/usr/include/c++/4.8.2/bits/stl_tree.h:140:49: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
    _M_value_field(std::forward<_Args>(__args)...) { }
                                                 ^
In file included from /usr/include/c++/4.8.2/memory:81:0,
                 from test.cpp:1:
/usr/include/c++/4.8.2/bits/unique_ptr.h:273:7: error: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^

然而,我在CentOS 7上的GCC 4.8.5明显不满意:

import a
import b

我需要做些什么来完成这项工作?

3 个答案:

答案 0 :(得分:12)

你基本上不能在C ++ 14中这样做,因为一个集合的元素是const。由于移动是一种修改操作,因此您需要某种方式来获取对元素的非const访问权限,而且根本无法做到这一点。

能够使用新的merge()成员函数在C ++ 17中执行此操作:

b.merge(std::move(a));

同样,会有一个extract()成员函数,它会删除并授予您对单个节点的非const访问权限。

当然,您总是可以使用类似的类型(h / t by T.C.):

struct Hack {
    mutable std::unique_ptr<T> p;
    T* raw_ptr;

    bool operator<(Hack const& h) const {
        // implemented in terms of the raw ptr
        // not the unique ptr
    }
};

p移动现在是安全的(它是可变的)并且只要你只是移动(而不是reset())并根据原始指针实现排序,那么你也应该保留集合顺序。你现在必须存储两个指针,但这应该避免在C ++ 11中使用UB。

答案 1 :(得分:4)

std :: set的元素是 const ,主要是因为std :: set按元素的值排序。更改值可能会改变排序 - 因此 - 将破坏std :: set的内部表示。

将std :: unique_ptr移出std :: set肯定会使std :: set无法使用。

答案 2 :(得分:0)

以下是在g ++ 4.8.2中使用C ++ 11执行此操作的一种方法:

#include <iostream>
#include <set>
#include <memory>
#include <utility>

// One possible solution:
template <typename Type>
void move_set_unique_ptr( 
    std::set<std::unique_ptr<Type>> & source,
    std::set<std::unique_ptr<Type>> & destination
) {
    for ( const std::unique_ptr<Type> & source_unique_ptr : source ) {
        destination.insert(
            std::move( const_cast<std::unique_ptr<Type> &>( source_unique_ptr ) )
        );
    }
    source.clear();
}

// Not part of a solution ... just for use with std::cout:
template <typename Type>
std::ostream & operator << (
    std::ostream & o,
    const std::set<std::unique_ptr<Type>> & s
);

int main() {
    using type = int;

    std::set<std::unique_ptr<type>> a;
    std::set<std::unique_ptr<type>> b;

    // std::make_unique<T> is not available in C++11

    a.insert( nullptr );
    a.insert( std::unique_ptr<type>( new type{ 62 } ) );
    a.insert( std::unique_ptr<type>( new type{ 42 } ) );
    a.insert( std::unique_ptr<type>( new type{ 22 } ) );

    b.insert( std::unique_ptr<type>( new type{ 41 } ) );
    b.insert( std::unique_ptr<type>( new type{ 42 } ) );
    b.insert( std::unique_ptr<type>( new type{ 43 } ) );
    b.insert( nullptr );

    std::cout << "a: " << a << '\n';
    std::cout << "b: " << b << '\n';

    move_set_unique_ptr(a, b);

    std::cout << "a: " << a << '\n';
    std::cout << "b: " << b << '\n';
}

// Not part of a solution ... just for use with std::cout:
template <typename Type>
std::ostream & operator << (
    std::ostream & o,
    const std::set<std::unique_ptr<Type>> & s
) {
    auto i = std::begin( s );
    auto end = std::end( s );
    o << '{';
    if ( i != end ) {
        auto print = [ &o, &i ]() {
            o << i->get() << ": " << ( *i == nullptr ? Type{} : **i );
        };
        o << ' '; print();
        for ( ++i; i != end; ++i ) {
            o << ", "; print();
        }
        o << ' ';
    }
    o << '}';
    return o;
}

这将输出如下内容:

a: { 0: 0, 0x8f3c50: 62, 0x8f3ca0: 42, 0x8f3cf0: 22 }
b: { 0: 0, 0x8f3d40: 41, 0x8f3d90: 42, 0x8f3de0: 43 }
a: {}
b: { 0: 0, 0x8f3c50: 62, 0x8f3ca0: 42, 0x8f3cf0: 22, 0x8f3d40: 41, 0x8f3d90: 42, 0x8f3de0: 43 }