我有一个Foo向量
list
Foo是一个内部得分较高的结构
vector<Foo> inputs
现在,我想按分数对输入进行排序,仅将获胜者分配给前3名。但是我不想更改原始输入向量。所以我想我需要创建一个参考向量然后进行排序?创建参考载体是否合法?有没有一种优雅的方法?
答案 0 :(得分: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;});
然后,您可以使用标准算法sort指针向量,而无需更改原始向量(如果需要):
// 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个元素。
答案 1 :(得分:2)
如果您确实不想修改原始向量,则必须将指针或索引的向量排序到原始向量中。要回答部分问题,没有办法建立参考向量,您不应该这样做。
要查找前三个(或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循环(如果您使用的是C ++ 17,也可以使用for_each_n
,如Christophe所述)。
答案 2 :(得分:2)
给出的唯一示例代码是针对指针的,并且只提到了更合适的IMO std::reference_wrapper
,没有指出如何在这种情况下使用它。我想解决这个问题!
非所有指针至少具有3个缺点:
&
,*
和->
为止; const
) ,执行重载解析或转换等工作-这些都不是您想要的。我敢肯定,每个人都在嘲笑这句话,并说“ 我永远不会犯这样愚蠢的错误”,但是您知道,在足够长的时间范围内,它会会发生。我通常更喜欢std::reference_wrapper
,
for
等)...尽管干扰现代对auto
的偏爱–至少直到我们得到建议的operator.
或operator auto
–并在其他情况下或者如果您只想使用更详细的.get()
避免这种不一致。不过,我认为这些皱纹既不会比指针的皱纹更糟,也不会永久存在,因为有各种积极的提议来美化使用包装/代理类型。我建议使用另一种词汇课程,尤其是对于公开显示的数据。有针对observer_ptr
的实验性建议,但是,如果您真的不想要指针式行为,那么您应该使用对引用建模的包装器...并且我们已经有了其中之一。
所以...可以这样重写接受的答案中的代码(现在使用#include
和我的格式设置):
#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; } );
}
这利用范围构造函数从实际reference_wrapper
的范围构造Foo
的范围,并在lambda参数列表中隐式转换为Foo&
,以避免必须执行reference_wrapper.get()
(然后,.
而不是->
的直接成员访问变得凌乱得多)。
当然,这可以概括为一:分解出可重用的辅助函数的主要候选方法是为任意vector< reference_wrapper<Foo> >
构造Foo
,只给出一对迭代器至{ {1}}。但是我们总是必须留一些东西作为练习给读者。 :P
答案 3 :(得分:1)
回答有关其面值的问题:
引用向量(以及它们的内置数组)在C ++中不合法。这是数组的规范性标准措辞:
不得存在对引用的引用,引用数组, 并且没有指向引用的指针。
对于矢量,禁止使用矢量元素必须是可分配的(而引用不是)的事实。
要具有间接对象的数组或向量,可以使用非所有者指针(std::vector<int*>
),或者,如果需要非指针访问语法,则可以使用包装器-std::reference_wrapper
答案 4 :(得分:1)
所以我想我需要创建一个参考向量然后进行排序?创建参照向量是否合法?
否,不可能有参考向量。有std::reference_wrapper
用于此目的,或者您可以使用裸指针。
除了Christophe展示的两种方式外,另一种方式是变换迭代器适配器,可以使用std::partial_sort_copy
将前3个指针/引用包装器排序为一个数组。
转换迭代器只需调用一个函数即可在赋值时转换输入,从而简单地调整输出迭代器。虽然标准库中没有迭代器适配器,所以您需要自己实现一个或使用一个库。