如果我有这样的课程C
:
class C {
std::string s;
public:
C(std::string& s) : s(s) {}
C(std::string&& s) : C(s) {}
};
我是否通过调用string&
构造函数中的string&&
构造函数来丢弃移动语义?
答案 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;是所谓的“通用引用”,这意味着它可以将其类型解析为TBar
,TBar&
或TBar&&
(您还可以添加const
和{混合中的{1}}。 volatile
就像它的名字所说的那样,并且“转发”任何TBar解析为(保留其推导类型)到字符串的构造函数。