我有以下代码:
struct Foo {
int var1;
int var2;
friend std::ostream& operator<<(std::ostream& os, const Foo& s){
return os << "[Foo] " << s.var1 << "," << s.var2 ;
}
};
int main() {
Foo foo;
foo.var1 = 1;
foo.var2 = 2;
std::list<Foo> list;
list.push_back(foo);
Foo &foo2 = list.front();
foo2.var2 = 5;
std::cout << "foo (" << &foo << "): " << foo << std::endl;
std::cout << "foo2 (foo from list) (" << &list.front() << "): " << foo2 << std::endl;
}
我希望foo
和foo2
都引用同一个对象。因此,当我将5
分配给foo2.var2
时,我也希望修改foo.var2
。然而,正如我们在以下输出中所看到的那样,这种情况并没有发生:
foo (0x7fffffffe140): [Foo] 1,2
foo2 (foo from list) (0x61ac30): [Foo] 1,5
这样做的正确方法是什么?
答案 0 :(得分:16)
使用push_back
将元素插入列表时,push_back
会创建一个插入列表的副本。解决方案是使用std::reference_wrapper
作为列表的基础类型,例如
std::list<std::reference_wrapper<Foo>> lst;
然后像
一样推进它lst.push_back(foo);
这是一个超级简单的例子,向您展示它是如何工作的:
#include <functional>
#include <iostream>
#include <list>
int main()
{
int i = 42;
std::list<std::reference_wrapper<int>> lst;
lst.push_back(i); // we add a "reference" into the list
lst.front().get() = 10; // we update the list
std::cout << i; // the initial i was modified!
}
您需要reference_wrapper
,因为您无法简单地创建引用列表,例如std::list<Foo&>
。或者,您可以使用指针,但我发现reference_wrapper
方法更透明。
在上面的简单示例中,请注意需要使用std::reference_wrapper::get()
来获取基础引用,因为reference_wrapper
位于赋值运算符的左侧,因此不会隐式通过[int
转换为std::reference_wrapper::operator T&
。
以下是修改为使用reference_wrapper
的完整工作代码:
答案 1 :(得分:4)
您的std::list<Foo>
包含实际的Foo
个对象。当您致电list.push_back(foo)
时,它会生成foo
的副本。然后,您声明foo2
引用该复制的对象,而不是原始对象。这就是foo
成员更改后foo2
未更新的原因。
尝试使用指针:
int main() {
Foo foo;
foo.var1 = 1;
foo.var2 = 2;
std::list<Foo*> list;
list.push_back(&foo);
Foo *foo2 = list.front();
foo2->var2 = 5;
std::cout << "foo (" << &foo << "): " << foo << std::endl;
std::cout << "foo from list (" << foo2 << "): " << *foo2 << std::endl;
}
答案 2 :(得分:2)
如果您将输出更改为:
std::cout << "foo " << foo << std::endl;
std::cout << "front " << list.front() << std::endl;
std::cout << "foo2 " << foo2 << std::endl;
您会发现输出如下所示:
foo [Foo] 1,2
front [Foo] 1,5
foo2 [Foo] 1,5
确实,当你得到一个引用,然后设置foo2
时,你确实改变了列表的前面!令人惊讶的是,列表的前面不再等于foo
。当您将某些内容推送到列表中时,它会将副本放在该列表中,而不是引用。
答案 3 :(得分:2)
为此,您需要引用语义而不是值语义。实现引用语义最直接的方法是通过指针。您的示例中只会进行一些小的更改:
struct Foo {
int var1;
int var2;
friend std::ostream& operator<<(std::ostream& os, const Foo& s){
return os << "[Foo] " << s.var1 << "," << s.var2 ;
}
};
int main() {
auto foo = std::make_unique<Foo>();
foo->var1 = 1;
foo->var2 = 2;
// We want a non owning observer of foo
auto&& foo2 = *foo;
std::list<std::unique_ptr<Foo>> list;
// The ownership of foo is moved into the container
list.emplace_back(std::move(foo));
// Another non owning observer
auto&& foo3 = *list.front();
foo3.var2 = 5;
// foo is null here, since we moved it in the container, but we have foo2
std::cout << "foo (" << &foo2 << "): " << foo2 << std::endl;
std::cout << "foo from list (" << list.front().get() << "): " << foo3 << std::endl;
}