std::set 成员变量在 class 实例移动时复制而不是移动
std::set member variable copied instead of moved when class instance moved
我一直在使用一对 classes。前者存储元数据,后者作为容器,支持基于元数据的各种索引。精简版发布在下方。
后者 class 使用 std::set 来管理前者 class 的对象集合,原因与引用稳定性相关(指向组合元数据对象的指针必须保持有效,因为添加和删除元素)。
由于我不明白的原因,索引 class 的集合成员正在调用其(已删除的)复制构造函数,即使应该调用移动语义也是如此。我在 Apple LLVM 7.0.0(使用 libc++)和 GCC 4.9(libstdc++)上编译并收到类似的错误。
在这种情况下是否有某些原因无法调用移动构造函数?
#include <ctime>
#include <functional>
#include <set>
#include <string>
#include <memory>
#include <vector>
// used to store meta data about some class T
template<typename T>
struct Foo {
std::unique_ptr<T> data; // T being some abstract class
std::string label;
std::time_t stamp;
const Foo* parentPtr;
Foo( std::unique_ptr<T>&& data,
const std::string& label,
const std::time_t stamp,
const Foo* parent ) : data( std::move(data) ),
label( label ),
stamp( stamp ),
parentPtr( parent ){}
};
// ordering of Foo objects should be in terms of the label. Necessary for std::set.
namespace std {
template<typename T>
struct less< Foo<T> > {
constexpr bool
operator()( const Foo<T>& lhs,
const Foo<T>& rhs ) const {
return lhs.label < rhs.label;
}
};
}
// collection of Foos
template<typename T>
class Bar{
public:
Bar() = default;
Bar( std::set< Foo<T> >&& foos ) : foos( std::move(foos) ){}
~Bar() = default;
// irrelevant helpers omitted
std::set< Foo<T> > foos;
};
// causes compilation to fail
auto returnTest0 = [](){
std::set< Foo< std::vector<int> > > S = {};
return Bar< std::vector<int> >(std::move(S));
// This is an r-value. Why isn't RVO or move semantics occuring?
};
// also causes compilation to fail
auto returnTest1 = [](Bar< std::vector<int> >&& b){
return std::move(b);
};
下面重现了 gcc 4.9 的编译器错误
In file included from /usr/include/x86_64-linux-gnu/c++/4.9/bits/c++allocator.h:33:0,
from /usr/include/c++/4.9/bits/allocator.h:46,
from /usr/include/c++/4.9/string:41,
from /usr/include/c++/4.9/stdexcept:39,
from /usr/include/c++/4.9/array:38,
from /usr/include/c++/4.9/tuple:39,
from /usr/include/c++/4.9/functional:55,
from 2:
/usr/include/c++/4.9/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = Foo<std::vector<int> >; _Args = {const Foo<std::vector<int, std::allocator<int> > >&}; _Tp = std::_Rb_tree_node<Foo<std::vector<int> > >]':
/usr/include/c++/4.9/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 = Foo<std::vector<int> >; _Args = {const Foo<std::vector<int, std::allocator<int> > >&}; _Alloc = std::allocator<std::_Rb_tree_node<Foo<std::vector<int> > > >; std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> = void]'
/usr/include/c++/4.9/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 = Foo<std::vector<int> >; _Args = {const Foo<std::vector<int, std::allocator<int> > >&}; _Alloc = std::allocator<std::_Rb_tree_node<Foo<std::vector<int> > > >; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]'
/usr/include/c++/4.9/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 Foo<std::vector<int, std::allocator<int> > >&}; _Key = Foo<std::vector<int> >; _Val = Foo<std::vector<int> >; _KeyOfValue = std::_Identity<Foo<std::vector<int> > >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<Foo<std::vector<int> > >*]'
/usr/include/c++/4.9/bits/stl_tree.h:445:53: required from 'std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_clone_node(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type) [with _Key = Foo<std::vector<int> >; _Val = Foo<std::vector<int> >; _KeyOfValue = std::_Identity<Foo<std::vector<int> > >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<Foo<std::vector<int> > >*; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type = const std::_Rb_tree_node<Foo<std::vector<int> > >*]'
/usr/include/c++/4.9/bits/stl_tree.h:1207:43: required from 'std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_copy(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type, std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type) [with _Key = Foo<std::vector<int> >; _Val = Foo<std::vector<int> >; _KeyOfValue = std::_Identity<Foo<std::vector<int> > >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<Foo<std::vector<int> > >*; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type = const std::_Rb_tree_node<Foo<std::vector<int> > >*]'
/usr/include/c++/4.9/bits/stl_tree.h:676:50: required from 'std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Rb_tree(const std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&) [with _Key = Foo<std::vector<int> >; _Val = Foo<std::vector<int> >; _KeyOfValue = std::_Identity<Foo<std::vector<int> > >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >]'
/usr/include/c++/4.9/bits/stl_set.h:197:22: required from 'std::set<_Key, _Compare, _Alloc>::set(const std::set<_Key, _Compare, _Alloc>&) [with _Key = Foo<std::vector<int> >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >]'
36:7: required from here
/usr/include/c++/4.9/ext/new_allocator.h:120:4: error: use of deleted function 'Foo<std::vector<int> >::Foo(const Foo<std::vector<int> >&)'
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^
9:8: note: 'Foo<std::vector<int> >::Foo(const Foo<std::vector<int> >&)' is implicitly deleted because the default definition would be ill-formed:
9:8: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = std::vector<int>; _Dp = std::default_delete<std::vector<int> >]'
In file included from /usr/include/c++/4.9/memory:81:0,
from 5:
/usr/include/c++/4.9/bits/unique_ptr.h:356:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^
用户声明的析构函数
~Bar() = default;
禁止隐式生成 Bar
的移动构造函数和移动赋值运算符。
要么删除析构函数声明,要么显式默认移动特殊成员:
Bar(Bar&&) = default;
Bar& operator=(Bar&&) = default;
我一直在使用一对 classes。前者存储元数据,后者作为容器,支持基于元数据的各种索引。精简版发布在下方。
后者 class 使用 std::set 来管理前者 class 的对象集合,原因与引用稳定性相关(指向组合元数据对象的指针必须保持有效,因为添加和删除元素)。
由于我不明白的原因,索引 class 的集合成员正在调用其(已删除的)复制构造函数,即使应该调用移动语义也是如此。我在 Apple LLVM 7.0.0(使用 libc++)和 GCC 4.9(libstdc++)上编译并收到类似的错误。
在这种情况下是否有某些原因无法调用移动构造函数?
#include <ctime>
#include <functional>
#include <set>
#include <string>
#include <memory>
#include <vector>
// used to store meta data about some class T
template<typename T>
struct Foo {
std::unique_ptr<T> data; // T being some abstract class
std::string label;
std::time_t stamp;
const Foo* parentPtr;
Foo( std::unique_ptr<T>&& data,
const std::string& label,
const std::time_t stamp,
const Foo* parent ) : data( std::move(data) ),
label( label ),
stamp( stamp ),
parentPtr( parent ){}
};
// ordering of Foo objects should be in terms of the label. Necessary for std::set.
namespace std {
template<typename T>
struct less< Foo<T> > {
constexpr bool
operator()( const Foo<T>& lhs,
const Foo<T>& rhs ) const {
return lhs.label < rhs.label;
}
};
}
// collection of Foos
template<typename T>
class Bar{
public:
Bar() = default;
Bar( std::set< Foo<T> >&& foos ) : foos( std::move(foos) ){}
~Bar() = default;
// irrelevant helpers omitted
std::set< Foo<T> > foos;
};
// causes compilation to fail
auto returnTest0 = [](){
std::set< Foo< std::vector<int> > > S = {};
return Bar< std::vector<int> >(std::move(S));
// This is an r-value. Why isn't RVO or move semantics occuring?
};
// also causes compilation to fail
auto returnTest1 = [](Bar< std::vector<int> >&& b){
return std::move(b);
};
下面重现了 gcc 4.9 的编译器错误
In file included from /usr/include/x86_64-linux-gnu/c++/4.9/bits/c++allocator.h:33:0,
from /usr/include/c++/4.9/bits/allocator.h:46,
from /usr/include/c++/4.9/string:41,
from /usr/include/c++/4.9/stdexcept:39,
from /usr/include/c++/4.9/array:38,
from /usr/include/c++/4.9/tuple:39,
from /usr/include/c++/4.9/functional:55,
from 2:
/usr/include/c++/4.9/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = Foo<std::vector<int> >; _Args = {const Foo<std::vector<int, std::allocator<int> > >&}; _Tp = std::_Rb_tree_node<Foo<std::vector<int> > >]':
/usr/include/c++/4.9/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 = Foo<std::vector<int> >; _Args = {const Foo<std::vector<int, std::allocator<int> > >&}; _Alloc = std::allocator<std::_Rb_tree_node<Foo<std::vector<int> > > >; std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> = void]'
/usr/include/c++/4.9/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 = Foo<std::vector<int> >; _Args = {const Foo<std::vector<int, std::allocator<int> > >&}; _Alloc = std::allocator<std::_Rb_tree_node<Foo<std::vector<int> > > >; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]'
/usr/include/c++/4.9/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 Foo<std::vector<int, std::allocator<int> > >&}; _Key = Foo<std::vector<int> >; _Val = Foo<std::vector<int> >; _KeyOfValue = std::_Identity<Foo<std::vector<int> > >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<Foo<std::vector<int> > >*]'
/usr/include/c++/4.9/bits/stl_tree.h:445:53: required from 'std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_clone_node(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type) [with _Key = Foo<std::vector<int> >; _Val = Foo<std::vector<int> >; _KeyOfValue = std::_Identity<Foo<std::vector<int> > >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<Foo<std::vector<int> > >*; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type = const std::_Rb_tree_node<Foo<std::vector<int> > >*]'
/usr/include/c++/4.9/bits/stl_tree.h:1207:43: required from 'std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_copy(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type, std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type) [with _Key = Foo<std::vector<int> >; _Val = Foo<std::vector<int> >; _KeyOfValue = std::_Identity<Foo<std::vector<int> > >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<Foo<std::vector<int> > >*; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Const_Link_type = const std::_Rb_tree_node<Foo<std::vector<int> > >*]'
/usr/include/c++/4.9/bits/stl_tree.h:676:50: required from 'std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Rb_tree(const std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&) [with _Key = Foo<std::vector<int> >; _Val = Foo<std::vector<int> >; _KeyOfValue = std::_Identity<Foo<std::vector<int> > >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >]'
/usr/include/c++/4.9/bits/stl_set.h:197:22: required from 'std::set<_Key, _Compare, _Alloc>::set(const std::set<_Key, _Compare, _Alloc>&) [with _Key = Foo<std::vector<int> >; _Compare = std::less<Foo<std::vector<int> > >; _Alloc = std::allocator<Foo<std::vector<int> > >]'
36:7: required from here
/usr/include/c++/4.9/ext/new_allocator.h:120:4: error: use of deleted function 'Foo<std::vector<int> >::Foo(const Foo<std::vector<int> >&)'
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^
9:8: note: 'Foo<std::vector<int> >::Foo(const Foo<std::vector<int> >&)' is implicitly deleted because the default definition would be ill-formed:
9:8: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = std::vector<int>; _Dp = std::default_delete<std::vector<int> >]'
In file included from /usr/include/c++/4.9/memory:81:0,
from 5:
/usr/include/c++/4.9/bits/unique_ptr.h:356:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^
用户声明的析构函数
~Bar() = default;
禁止隐式生成 Bar
的移动构造函数和移动赋值运算符。
要么删除析构函数声明,要么显式默认移动特殊成员:
Bar(Bar&&) = default;
Bar& operator=(Bar&&) = default;