我可以列出初始化只移动类型的向量吗?

新手上路,请多包涵

如果我通过我的 GCC 4.7 快照传递以下代码,它会尝试将 unique_ptr s 复制到向量中。

 #include <vector>
#include <memory>

int main() {
    using move_only = std::unique_ptr<int>;
    std::vector<move_only> v { move_only(), move_only(), move_only() };
}

显然,这是行不通的,因为 std::unique_ptr 不可复制:

错误:使用已删除的函数 ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete; std::unique_ptr<_Tp, _Dp> = std::unique_ptr]’

GCC 尝试从初始化列表中复制指针是否正确?

原文由 R. Martinho Fernandes 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 340
2 个回答

18.9 中 <initializer_list> 的概要清楚地表明,初始化列表的元素始终通过 const-reference 传递。不幸的是,在当前版本的语言中,似乎没有任何方法可以在初始化列表元素中使用移动语义。

具体来说,我们有:

 typedef const E& reference;
typedef const E& const_reference;

typedef const E* iterator;
typedef const E* const_iterator;

const E* begin() const noexcept; // first element
const E* end() const noexcept; // one past the last element

原文由 Kerrek SB 发布,翻译遵循 CC BY-SA 3.0 许可协议

编辑: 由于@Johannes 似乎不想发布最佳解决方案作为答案,所以我会这样做。

 #include <iterator>
#include <vector>
#include <memory>

int main(){
  using move_only = std::unique_ptr<int>;
  move_only init[] = { move_only(), move_only(), move_only() };
  std::vector<move_only> v{std::make_move_iterator(std::begin(init)),
      std::make_move_iterator(std::end(init))};
}

std::make_move_iterator 返回的迭代器将在取消引用时移动指向的元素。


原始答案: 我们将在这里使用一个小助手类型:

 #include <utility>
#include <type_traits>

template<class T>
struct rref_wrapper
{ // CAUTION - very volatile, use with care
  explicit rref_wrapper(T&& v)
    : _val(std::move(v)) {}

  explicit operator T() const{
    return T{ std::move(_val) };
  }

private:
  T&& _val;
};

// only usable on temporaries
template<class T>
typename std::enable_if<
  !std::is_lvalue_reference<T>::value,
  rref_wrapper<T>
>::type rref(T&& v){
  return rref_wrapper<T>(std::move(v));
}

// lvalue reference can go away
template<class T>
void rref(T&) = delete;

可悲的是,这里的直接代码不起作用:

 std::vector<move_only> v{ rref(move_only()), rref(move_only()), rref(move_only()) };

由于标准,无论出于何种原因,都没有定义这样的转换复制构造函数:

 // in class initializer_list
template<class U>
initializer_list(initializer_list<U> const& other);

The initializer_list<rref_wrapper<move_only>> created by the brace-init-list ( {...} ) won’t convert to the initializer_list<move_only> that the vector<move_only> takes.所以我们这里需要两步初始化:

 std::initializer_list<rref_wrapper<move_only>> il{ rref(move_only()),
                                                   rref(move_only()),
                                                   rref(move_only()) };
std::vector<move_only> v(il.begin(), il.end());

原文由 Xeo 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题