创建引用向量的优雅方式
Elegant way to create a vector of reference
我有一个Foo 的向量
vector<Foo> inputs
Foo是一个在中有一些分数的结构
struct Foo {
...
float score
bool winner
}
现在我想按分数对输入进行排序,只将获胜者分配给前3名。但是我不想改变原始的输入向量。所以我想我需要创建一个参考向量,然后对其进行排序?创建引用向量合法吗?有没有一种优雅的方法可以做到这一点?
这里有两种不同的创建vector<Foo*>
:的方法
vector<Foo*> foor;
for (auto& x:inputs)
foor.push_back(&x);
vector<Foo*> foob(inputs.size(),nullptr);
transform(inputs.begin(), inputs.end(), foob.begin(), [](auto&x) {return &x;});
然后,您可以使用标准算法对指针向量进行排序,而无需更改原始向量(如果需要):
// decreasing order according to score
sort(foob.begin(), foob.end(), [](Foo*a, Foo*b)->bool {return a->score>b->score;});
最后,您可以使用for_each_n()
算法(如果是C++17)或简单地使用普通循环来更改前n个元素。
在线演示
给出的唯一示例代码是指针,只提到了更适合IMO的std::reference_wrapper
,没有说明在这种情况下如何使用它。我想解决这个问题!
非拥有指针至少有3个缺点:
- 视觉,从不得不在代码中使用
&
、*
和->
- 实用性:如果您只想引用一个对象,那么现在您就有了一个可以从其他指针中减去的东西(可能不相关)、递增/递减(如果不是
const
)、进行重载解析或转换等。–没有你想要的。我相信每个人都在嘲笑这件事,并说"我永远不会犯这样愚蠢的错误",但你知道,在足够长的时间内,会发生 - 以及缺乏自我文档,因为它们没有所有权的固有语义或缺乏所有权
我通常更喜欢std::reference_wrapper
,哪个
- 清楚地自我记录了其纯粹的观察语义
- 只能产生对对象的引用,因此没有任何类似指针的陷阱,并且
- 通过隐式转换为真正引用的类型,避免了许多语法问题,从而最大限度地减少了可以调用转换的运算符噪声(传递到函数、初始化引用、范围-
for
等)……尽管干扰了auto
–至少直到我们得到建议的CCD_ 11或operator auto
–在其他情况下,或者如果您只是想避免这种不一致,则需要更详细的.get()
。尽管如此,我认为这些褶皱既不比指针的褶皱更糟糕,也不可能是永久性的,因为有各种积极的提议来美化包装器/代理类型的使用
我建议使用那个或另一个词汇类,尤其是对于公开的数据。有observer_ptr
之类的实验性建议,但同样,如果你真的不想要类似指针的行为,那么你应该使用一个为引用建模的包装器。。。我们已经有一个了。
所以。。。接受的答案中的代码可以这样重写(现在有了#include
s和我对格式化的偏好):
#include <algorithm>
#include <functional>
#include <vector>
// ...
void
modify_top_n(std::vector<Foo>& v, int const n)
{
std::vector< std::reference_wrapper<Foo> > tmp{ v.begin(), v.end() };
std::nth_element( tmp.begin(), tmp.begin() + n, tmp.end(),
[](Foo const& f1, Foo const& f2){ return f1.score > f2.score; } );
std::for_each( tmp.begin(), tmp.begin() + n,
[](Foo& f){ f.winner = true; } );
}
这使用了范围构造函数从实际的Foo
s的范围构造reference_wrapper
s的范围,并在lambda参数列表中隐式转换为Foo&
以避免必须执行reference_wrapper.get()
(然后我们可以通过.
而不是->
进行混乱得多的直接成员访问)。
当然,这是可以概括的:分解为可重用辅助函数的主要候选者是为任意Foo
构造vector< reference_wrapper<Foo> >
,只给-Foo
一对迭代器。但我们总是要把一些东西留给读者练习P
如果您真的不想修改原始向量,那么您必须将指针或索引的向量排序到原始向量中。要回答你的部分问题,不,没有办法制作一个参考向量,你不应该这样做
要查找前三个(或n
)元素,甚至不必对整个向量进行排序。STL让你覆盖了std::nth_element
(或者std::partial_sort
,如果你关心顶部元素的顺序),你可以这样做:
void modify_top_n(std::vector<Foo> &v, int n) {
std::vector<Foo*> tmp(v.size());
std::transform(v.begin(), v.end(), tmp.begin(), [](Foo &f) { return &f; });
std::nth_element(tmp.begin(), tmp.begin() + n, tmp.end(),
[](const Foo* f1, const Foo *f2) { return f1->score > f2->score; });
std::for_each(tmp.begin(), tmp.begin() + n, [](Foo *f) {
f->winner = true;
});
}
假设向量至少具有n
条目。我使用for_each
只是因为当你有迭代器范围时更容易,你也可以使用for循环(或者Christophe提到的for_each_n
,如果你有C++17)。
回答关于其面值的问题:
引用的向量(以及它们的内置数组)在C++中是不合法的。以下是阵列的标准措辞:
不得引用引用,不得引用数组,并且没有指向引用的指针。
对于向量,向量元素必须是可赋值的(而引用不是),这是禁止的。
要拥有间接对象的数组或向量,可以使用非拥有指针(std::vector<int*>
),或者,如果需要非指针访问语法,可以使用包装器std::reference_wrapper
。
所以我想我需要创建一个引用向量,然后对其进行排序?创建引用向量合法吗?
不,不可能有引用向量。有std::reference_wrapper
用于此目的,或者您可以使用裸指针。
除了Christophe展示的两种方法之外,还有一种方法是转换迭代器适配器,它可以用于使用std::partial_sort_copy
将前3个指针/引用包装器排序为一个数组。
转换迭代器只是通过调用函数在赋值时转换输入来调整输出迭代器。不过,标准库中没有迭代器适配器,所以您需要自己实现一个,或者使用库。
- 创建引用向量的优雅方式
- 引用 std::any 或 not_yet_in_std::whatever 的惯用方式是什么?
- 使用 python 绑定来检查 C++ 类型是否是规范方式的指针、引用等?
- 包含具有无效位置的节点引用的方式
- 如何以编程方式查找从 html 引用的所有文件
- 有没有办法在函数以编程方式返回的引用上多次调用函数? -元组动态访问
- 与 C++ 相比,C# 中的按引用传递的工作方式是否正确?
- 从BoostCon谈话中重载取消引用运算符的奇怪方式
- 以这种方式返回对新创建的对象的引用,这是一种安全的做法
- 为什么不能以与指针相同的方式返回常量引用
- 这是使用移动引用和unique_ptr的正确方式吗
- 如何使用数组通过引用对对象进行排序?(以一种不那么愚蠢/复杂的方式.)
- 将指针/引用传递到构造函数中现有对象的首选方式是什么
- 返回对大小不变的指针数组的引用的最佳方式是什么
- 为什么引用unique_ptr会以这种方式行事
- 传递参数的方式——值vs引用vs指针
- 为渲染对象的所有实例维护一个指向共享纹理/资源的指针/引用的最佳方式
- 编译器会根据我处理未引用参数的方式做不同的事情吗
- 用c++的方式思考Java引用
- 了解引用变量的工作方式