2016-07-30 12 views
6

私は本当に他に1 std::setからいくつかのunique_ptr Sを移動したい:なぜ私はstd :: :: std :: sets間でstd :: unique_ptrsを移動できませんか?

#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())); 
} 

しかし、CentOSの7上の私のGCC 4.8.5が明らかに不幸である:

[[email protected] ~]# 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; 
    ^

何私はこの仕事をするために行う必要がありますか?

+0

_「私はやって何が必要ですこの作業をするには? "_それは新しいGCCバージョンで動作しますか? –

+0

@πάνταῥεῖ:[はっきりしない](http://coliru.stacked-crooked.com/a/909023cd2804d25c) –

+0

これは、GCCのバグか、より根本的な問題かを区別するのに役立ちます。標準に準拠したコードを適用していること –

答えて

12

セットの要素はconstなので、基本的にはC++ではできません。移動は変更操作なので、要素への非アクセスを取得するには何らかの方法が必要です。これを行う方法はありません。

しかし、あなた新しいmerge()メンバ関数をC++ 17をこの中に行うことができるようになります。同様に

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

、あなた以外を削除して与えるextract()メンバ関数が存在しますconst単一ノードへのアクセス。もちろん


、あなたはいつも(T.C.によってH/Tで)のようなタイプを使用することができます。pから移動

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 
    } 
}; 

は今、安全な(それは可変だ)と、限り、あなたはちょうどようですを移動して、生ポインタを使って順序付けを実装すると、設定された順序も保持する必要があります(reset()ではなく)。今は2つのポインタを格納する必要がありますが、C++ 11ではUBを避ける必要があります。

+0

しかし、私はセットから要素を取り除いています。なぜ不変性の鍵がそれに影響を及ぼすべきですか? –

+1

@Lightnessあなたは単純にそれらに非「コンス」アクセス権がありません。 – Barry

+0

標準ライブラリではありませんが、標準ライブラリに要素を移動するよう求めています。 –

4

std :: setの要素は、主にstd :: setが要素の値によって順序付けられるため、constです。値を変更すると、結果としてstd :: setの内部表現が破損する順序が変更される可能性があります。

std :: unique_ptrをstd :: setから移動すると、確実にstd :: setが使用できなくなります。ここで

0

はグラムでのC++ 11 ++ 4.8.2を使用して、これを行うための一つの方法です:

#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 } 
+0

しかし... UB !! –

+0

std :: moveとdestination.insert(std :: unique_ptr &&)を使用するために、std :: unique_ptr にconstをキャストしているだけなので、ここでは未定義の動作(UB)はありません。よく定義されている。さらに、source.clear()呼び出しの最初のstd :: unique_ptr 要素のstd :: moveの間で、要素の並び替えをトリガできるソースへの呼び出しはありません。したがって、すべての動作が定義されています。 –

+2

突然変異しないと約束したオブジェクトに対して突然変異操作を実行することについて明確に定義されていることはありますか? –

関連する問題