局部变量的右值引用和移动
rvalue reference and move of a local variable
在此代码段中,我分配了一个本地对象 B,我将其传递给另一个对象 C 的构造函数,后者将其作为右值引用。然后我将后者放入容器中。
当我检索 C 并将其成员 static_cast 返回给 B 时,值不正确(并且 valgrind 识别出 18 个错误!)
#include <iostream>
#include <memory>
#include <vector>
class A {
public:
virtual ~A() {};
};
class B: public A {
public:
B(int foo) : _foo(foo) {}
int _foo;
};
class C {
public:
C(A&& a): _a(std::move(a)) {}
A&& _a;
};
std::vector<C> v;
void bar() {
v.emplace_back(B(12));
}
int main() {
bar();
C&& c = std::move(v.front());
std::cout << static_cast<B&&>(c._a)._foo << std::endl;
return 0;
}
我的理解是,由于 C 采用右值引用,因此不应该有任何对象切片。我应该仍然能够检索完整的 B 对象。此外,我对 std::move
的理解(很可能是有缺陷的)是我可以 'move' 脱离其上下文的局部变量,并且通过采用右值引用,C 获得 B 的所有权。
输出是 39931504
而不是 12
。
谁能给我解释一下这是怎么回事?
I should still be able to retrieve a full B object.
如果它不是一个在控件到达 bar
的右大括号时不存在的临时文件,您就会这样做。您在 C
中持有一个引用,对临时对象的引用很快就会变得悬空。
一个快速修复方法是简单地将 C
的成员声明更改为 std::unique_ptr<A> a;
并正确初始化它,例如
template<class AChild> C(AChild child)
: a(std::make_unique<AChild>(std::move(child))) {};
(按口味添加 SFINAE)。
问题是 class C
中对 A
的引用(即数据成员 _a
)比它所指的对象存在时间更长。这是因为 temporary B(12)
在你的 bar()
函数中
void bar() {
v.emplace_back(B(12));
}
从 bar()
返回后不存在。因此,class C
包含的对 A
的引用变成了 悬空引用 .
您可以使用 new
运算符创建 B
对象而不是临时对象:
void bar() {
B* ptr = new B(12);
v.emplace_back(std::move(*ptr));
}
从 bar()
返回时,此 B
对象不会停止存在,因此 C
中对 A
的引用仍然有效。
请注意,使用此方法您需要手动销毁对象:
C&& c = std::move(v.front());
// ...
delete &c._a; // destroy object
在此代码段中,我分配了一个本地对象 B,我将其传递给另一个对象 C 的构造函数,后者将其作为右值引用。然后我将后者放入容器中。
当我检索 C 并将其成员 static_cast 返回给 B 时,值不正确(并且 valgrind 识别出 18 个错误!)
#include <iostream>
#include <memory>
#include <vector>
class A {
public:
virtual ~A() {};
};
class B: public A {
public:
B(int foo) : _foo(foo) {}
int _foo;
};
class C {
public:
C(A&& a): _a(std::move(a)) {}
A&& _a;
};
std::vector<C> v;
void bar() {
v.emplace_back(B(12));
}
int main() {
bar();
C&& c = std::move(v.front());
std::cout << static_cast<B&&>(c._a)._foo << std::endl;
return 0;
}
我的理解是,由于 C 采用右值引用,因此不应该有任何对象切片。我应该仍然能够检索完整的 B 对象。此外,我对 std::move
的理解(很可能是有缺陷的)是我可以 'move' 脱离其上下文的局部变量,并且通过采用右值引用,C 获得 B 的所有权。
输出是 39931504
而不是 12
。
谁能给我解释一下这是怎么回事?
I should still be able to retrieve a full B object.
如果它不是一个在控件到达 bar
的右大括号时不存在的临时文件,您就会这样做。您在 C
中持有一个引用,对临时对象的引用很快就会变得悬空。
一个快速修复方法是简单地将 C
的成员声明更改为 std::unique_ptr<A> a;
并正确初始化它,例如
template<class AChild> C(AChild child)
: a(std::make_unique<AChild>(std::move(child))) {};
(按口味添加 SFINAE)。
问题是 class C
中对 A
的引用(即数据成员 _a
)比它所指的对象存在时间更长。这是因为 temporary B(12)
在你的 bar()
函数中
void bar() {
v.emplace_back(B(12));
}
从 bar()
返回后不存在。因此,class C
包含的对 A
的引用变成了 悬空引用 .
您可以使用 new
运算符创建 B
对象而不是临时对象:
void bar() {
B* ptr = new B(12);
v.emplace_back(std::move(*ptr));
}
从 bar()
返回时,此 B
对象不会停止存在,因此 C
中对 A
的引用仍然有效。
请注意,使用此方法您需要手动销毁对象:
C&& c = std::move(v.front());
// ...
delete &c._a; // destroy object