2011-11-03 57 views
62

我有某一类A的对象std::vector。类是不平凡的,有拷贝构造函数和定义移动的构造。如何在矢量增长时执行移动语义?

std::vector<A> myvec; 

如果我填充了A对象载体(使用例如myvec.push_back(a)),矢量的大小将增加,使用拷贝构造A(const A&)实例化于载体的元素的新副本。

我可以采用某种强制执行A类的移动构造函数beging代替?

+4

您可以通过使用移动感知向量执行。 –

+1

你可以请更具体的如何做到这一点? –

+1

您只需使用移动感知矢量实现。这听起来像你的标准库实现(这是它的btw?)不移动感知。您可以尝试使用Boost的移动感知容器。 –

回答

84

您需要通知C++(特别是std::vector)您的移动构造函数和析构函数不会抛出,使用noexcept。然后,移动构造函数将在矢量增长时被调用。

这是如何声明和实现是由std::vector尊重的举动constuctor:

A(A && rhs) noexcept { 
    std::cout << "i am the move constr" <<std::endl; 
    ... some code doing the move ... 
    m_value=std::move(rhs.m_value) ; // etc... 
} 

如果构造不是noexceptstd::vector不能用了,从此再不能保证例外保证标准要求。

更多关于什么标准说,读 C++ Move semantics and Exceptions

信用博谁暗示它可能有例外的事情。如果可能,也考虑Kerrek SB的建议并使用emplace_back。它可以更快(但通常不是),它可以更清晰,更紧凑,但也有一些缺陷(尤其是与非显式构造函数)。

编辑,通常默认是你想要的:移动一切可以移动的,复制剩下的。 Is the default Move constructor defined as noexcept?

注意的Visual Studio 2015年及以上的早期版本不支持,尽管它支持移动语义:要明确要求的是,写

A(A && rhs) = default; 

这样做,你会尽可能得到noexcept 。

+11

注意:'throw()'不推荐使用,而是使用'noexcept'。 –

+0

@MatthieuM。啊,是的 - 现在解决了。发现得好。 –

+0

出于兴趣,如何_does_ impl“知道”value_type的move ctor是否是'noexcept'?当调用范围也是'noexcept'函数时,语言是否可以限制函数调用候选集呢? –

14

有趣的是,GCC 4.7.2矢量只使用移动的构造,如果移动构造函数和析构函数都是noexcept。一个简单的例子:

struct foo { 
    foo() {} 
    foo(const foo &) noexcept { std::cout << "copy\n"; } 
    foo(foo &&) noexcept { std::cout << "move\n"; } 
    ~foo() noexcept {} 
}; 

int main() { 
    std::vector<foo> v; 
    for (int i = 0; i < 3; ++i) v.emplace_back(); 
} 

它输出的预期:

move 
move 
move 

然而,当我从~foo()删除noexcept,其结果是不同的:

copy 
copy 
copy 

我想这也回答this question

+0

在我看来,其他答案只谈移动构造函数,而不是关于_destructor_必须是noexcept。 –

+2

好点,但是析构函数在默认情况下是noexcept。 –

+0

呃,应该是,但事实证明,在gcc 4.7.2中并不是这样。所以这个问题实际上只针对gcc。不过,它应该在gcc 4.8.0中修复。参见[相关的stackoverflow问题](http://stackoverflow.com/questions/15721544/destructors-and-noexcept)。 –

0

看来,只有这样(对于C++ 17和早期版本),强制执行std::vector在重新分配时使用移动语义是删除复制构造函数:)。通过这种方式,它将使用你的移动构造函数或在编译时尝试:)。

有许多规章制度std::vector绝不能使用在重新分配移动构造函数,但没有关于它必须使用方法它。

template<class T> 
class move_only : public T{ 
public: 
    move_only(){} 
    move_only(const move_only&) = delete; 
    move_only(move_only&&) noexcept {}; 
    ~move_only() noexcept {}; 

    using T::T; 
}; 

Live

template<class T> 
struct move_only{ 
    T value; 

    template<class Arg, class ...Args, typename = std::enable_if_t< 
      !std::is_same_v<move_only<T>&&, Arg > 
      && !std::is_same_v<const move_only<T>&, Arg > 
    >> 
    move_only(Arg&& arg, Args&&... args) 
     :value(std::forward<Arg>(arg), std::forward<Args>(args)...) 
    {} 

    move_only(){} 
    move_only(const move_only&) = delete; 
    move_only(move_only&& other) noexcept : value(std::move(other.value)) {};  
    ~move_only() noexcept {}; 
}; 

Live code

T类必须有noexcept移动构造函数/分配新建分配FY运营商和noexcept析构函数。否则,你会得到编译错误。

std::vector<move_only<MyClass>> vec; 
+0

不需要删除拷贝构造函数。如果移动构造函数不接受,它将被使用。 – balki

+0

@balki它可能被使用。标准现在不要求这个。以下是讨论https://groups.google.com/a/isocpp.org/forum/?utm_medium=email&utm_source=footer#!msg/std-proposals/j5URs5ZY3GI/AanXG977CAAJ – tower120