创建参考向量的优雅方式

时间:2018-10-30 18:49:38

标签: c++ algorithm sorting vector

我有一个Foo向量

list

Foo是一个内部得分较高的结构

vector<Foo> inputs

现在,我想按分数对输入进行排序,仅将获胜者分配给前3名。但是我不想更改原始输入向量。所以我想我需要创建一个参考向量然后进行排序?创建参考载体是否合法?有没有一种优雅的方法?

5 个答案:

答案 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个元素。

Online demo

答案 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个指针/引用包装器排序为一个数组。

转换迭代器只需调用一个函数即可在赋值时转换输入,从而简单地调整输出迭代器。虽然标准库中没有迭代器适配器,所以您需要自己实现一个或使用一个库。