移动构造函数的这种实现是否会抛弃移动语义?

时间:2014-07-16 06:27:45

标签: c++ move-semantics

如果我有这样的课程C

class C {
    std::string s;

public:
    C(std::string&  s) : s(s) {}
    C(std::string&& s) : C(s) {}
};

我是否通过调用string&构造函数中的string&&构造函数来丢弃移动语义?

2 个答案:

答案 0 :(得分:2)

是的,你正在抛弃你的移动语义。我可以提供的最直接的方法来演示如何通过示例。这是我能想到的最好的事情,所以我希望很明显发生了什么。

考虑一下:

#include <iostream>

struct S
{
    int x;

    S() :x(1) { std::cout << __PRETTY_FUNCTION__ << '\n';}
    S(const S& s) : x(s.x) { std::cout << __PRETTY_FUNCTION__ << '\n';}
    S(S&& s) : x(std::move(s.x)) { ++x, s.x=0; std::cout << __PRETTY_FUNCTION__ << '\n';}
    ~S() { std::cout << __PRETTY_FUNCTION__ << ':' << x << '\n';}
};

class C {
    S s;

public:
    C(S& s) : s(s) { std::cout << __PRETTY_FUNCTION__ << '\n';}
    C(S&& s) : C(s) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    ~C() { std::cout << __PRETTY_FUNCTION__ << '\n';}
};

int main()
{
    C c{S{}};
}

<强>输出

S::S()
S::S(const S &)
C::C(S &)
C::C(S &&)
S::~S():1
C::~C()
S::~S():1

请注意,两个实例S是通过移动构建的, 。任何“移动”到预定目标的S都会为0打印x。没有。第一个S是来自S{}的初始临时值;第二个是通过C(C&)初始化列表调用C(C&&)制作的副本。完成此代码后,当我们开始运行析构函数链时,存在两个完全构造的S

现在看看这个相同的代码,但在s成员上使用move-semantics:

#include <iostream>

struct S
{
    int x;

    S() :x(1) { std::cout << __PRETTY_FUNCTION__ << '\n';}
    S(const S& s) : x(s.x) { std::cout << __PRETTY_FUNCTION__ << '\n';}
    S(S&& s) : x(std::move(s.x)) { ++x, s.x=0; std::cout << __PRETTY_FUNCTION__ << '\n';}
    ~S() { std::cout << __PRETTY_FUNCTION__ << ':' << x << '\n';}
};

class C {
    S s;

public:
    C(S& s) : s(s) { std::cout << __PRETTY_FUNCTION__ << '\n';}
    C(S&& s) : s(std::move(s)) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
    ~C() { std::cout << __PRETTY_FUNCTION__ << '\n';}
};

int main()
{
    C c{S{}};
}

<强>输出

S::S()
S::S(S &&)
C::C(S &&)
S::~S():0
C::~C()
S::~S():2

仍然构造了S的两个实例,但后者是通过移动构造,吸取了第一个实例。当我们开始销毁所有这些东西时,只有S个实例中的一个仍然有效;另一个是通过搬出而死的。

长话短说,如果你的目标正在发展,那么你就是在射击自己。我希望有所帮助,但如果不这样做,我完全愿意抛弃它。

答案 1 :(得分:0)

当人们提到“完美转发”时,这不是所指的。这只是一个调用复制构造函数的移动构造函数(我假设你的意思)。这是完美的转发:

class Foo{
public:
    template<typename TBar> Foo(TBar&& bar): m_str(std::forward<TBar>(bar)){}

private:
    std::string m_str;
};

这里发生的是,由于模板参数推断和参考折叠规则,TBar&amp;&amp;是所谓的“通用引用”,这意味着它可以将其类型解析为TBarTBar&TBar&&(您还可以添加const和{混合中的{1}}。 volatile就像它的名字所说的那样,并且“转发”任何TBar解析为(保留其推导类型)到字符串的构造函数。