如何管理 std::list 元素作为引用
How to manage std::list elements as references?
我有以下代码:
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
正确的方法是什么?
使用 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
位于赋值运算符的左侧,因此不会通过 [ std::reference_wrapper::operator T&
隐式转换为 int
。
以下是修改为使用 reference_wrapper
s 的完整工作代码:
http://coliru.stacked-crooked.com/a/fb1fd67996d6e5e9
您的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;
}
为此,您需要引用语义而不是值语义。实现引用语义的最直接方法是通过指针。只有示例中的一些小更改才能做到这一点:
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;
}
如果将输出更改为以下内容:
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
。 当您将某些内容推送到列表中时,它会在该列表中放置一个副本,而不是引用。
- 使用 <list> (错误 C2760) 打印队列的元素
- list.push_back似乎复制了最后添加的元素
- 向 std::list 添加新元素的复杂性
- 将元素添加到 std::list 在多线程中,无需 C++ 互斥锁
- C++:我可以重用/移动 std::list 元素从中间到结尾吗?
- 使用容器中元素的别名删除带有 std::list::remove 的元素是否正确?
- 存储 std::list 元素的地址;内存
- 如何从该列表的元素中获取 std::list<T>::迭代器?
- 通过引用从 std::list 中删除元素
- 如何管理 std::list 元素作为引用
- boost::instrusive::list 带有自动取消链接钩子:我可以使用列表中的值来确定列表是否只有一个元素
- 如何从 std::list 获取特定元素
- 如何从 std::list 中删除元素?
- 我可以在 std::list 中移动元素而不会使迭代器或引用无效,但是如何移动呢?
- std::list - 删除元素
- 在C++中有条件地删除Singly Linked List中的元素
- 访问std::list元素将转储核心
- 无法将 std::set 中的元素迭代器插入到 std::list
- 为什么<list>元素会在使用 malloc() 内存的对象中导致分割错误
- 从迭代器返回一个std::list元素的引用