C++ 将对象传递给比较函数会使排序变慢吗?

C++ 将对象传递给比较函数会使排序变慢吗?,c++,sorting,C++,Sorting,我的程序中有以下代码 //Compare class class SortByFutureVolume { public: SortByFutureVolume(const Graph& _g): g(_g){} bool operator() (const Index& lhs, const Index& rhs){ return g.getNode(lhs).futureVolume() > g.getN

我的程序中有以下代码

//Compare class
class SortByFutureVolume
{
public:
        SortByFutureVolume(const Graph& _g): g(_g){}

        bool operator() (const Index& lhs, const Index& rhs){
            return g.getNode(lhs).futureVolume() > g.getNode(rhs).futureVolume();
        }
    private:
        Graph g;
};
然后我用它进行如下排序:

    std::sort(nodes.begin(), nodes.end(),SortByFutureVolume(g));
当我在我的mac电脑上运行23K大小的向量的上述代码时,它只需几秒钟就完成了。然而,当我在我的ubuntu 14机器上运行时。这需要几分钟,甚至还没有完成

我搜索这个问题,并在这里找到了以下解决方案

基本上修改我的代码可以解决这个问题:

SortByFutureVolume s(g);
std::sort(_nodes.begin(), _nodes.begin()+ end, std::ref(s));
在这之后,我的mac和ubuntu上的运行时间是相当的。非常快


我知道这很有效,但我想知道为什么?我知道上面的慢代码是由于复制了graph和SortByFutureVolume。为什么需要std::ref()?这个解决方案正确吗?有没有更好的方法

std::ref
是一个伪装的指针。代码所做的是,它不是复制一个重量级的
SortByFutureVolume
对象,而是在指针周围复制到同一个对象,这显然要快得多


该选项将使
图形g
成为分拣机对象内部的一个(常量)参考。

如果
g
可以只读,则应在
SortByFutureVolume
中设置
图形
常量图形&
。这样,只要复制了
SortByFutureVolume
,就不会复制
图形

class SortByFutureVolume
{
public:
        SortByFutureVolume(const Graph& _g): g(_g){}

        bool operator() (const Index& lhs, const Index& rhs){
            return g.getNode(lhs).futureVolume() > g.getNode(rhs).futureVolume();
        }
    private:
        Graph& g;
        // or
        const Graph& g;
};
正如注释中所指出的,如果将
SortByFutureVolume
更改为存储指向
图形的指针而不是referenece,则
SortByFutureVolume
将变为可复制分配,因为可以分配指针,但无法分配引用。那会给你

class SortByFutureVolume
{
public:
        SortByFutureVolume(const Graph& _g): g(&_g){}

        bool operator() (const Index& lhs, const Index& rhs){
            return g->getNode(lhs).futureVolume() > g->getNode(rhs).futureVolume();
        }
    private:
        const Graph * g;
};

另一方面,函数参数中可以使用
\u g
作为变量名,因为它不以大写字母开头,但最好不要使用前导下划线。在全局空间中,这一点更加正确,
\u g
将是一个无效的标识符,因为它是为实现保留的。

您的
SortByFutureVolume
在每次复制整个图形时都会对其进行复制,而std::sort会根据比较函数对象的值进行大量复制

请看这里:

对于简单的
std::vector
sort,它在内部对
SortByFutureVolume
类进行了20次实例化。您的图形可能被复制了相同的次数


std::ref
只复制对比较函数对象的引用-这将删除所有深度副本,从而加快整个过程。

您正在调用的std::sort变体的原型是

template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
当然,如果您有一个与C++11兼容的编译器,您可以使用lambda:

std::sort(nodes.begin(), nodes.end(), [&g](const Index& lhs, const Index& rhs) {
    return g.getNode(lhs).futureVolume() > g.getNode(rhs).futureVolume();
});

但是图形对象仍然被复制到函子
s
@juanchopanza中,从其他证据来看,这可能不是一个大的性能问题。我假设,
std::sort
implementation OP在每次迭代中都会复制一份sorter。这是有意义的。在c++11之前是如何做到这一点的?我对这门语言相当陌生。就像我说的那样-通过将
图g
作为类中实际对象的(const)引用。这就解决了它。谢谢。是的,同级别。然而,在使用htop可视化内存的mac上,我意识到我的mac使用了4个内核sorting@juanchopanza如果我通过引用传递,如何复制图形?您有一个缺陷:节点的迭代器不是节点的迭代器,而是一些
索引。使迭代器能够取消引用所需的节点。@unekwu它被复制到
g
Graph g
@DieterLücking我实际上是说vectorI会推荐一个指针。对象是可复制构造的,但不是可复制分配的,这没有多大意义。@BenjaminLindley,为什么?我知道很多东西都是这样的。还有一个指针会阻止分类器使用临时对象。@SergeyA:你为什么要这样做?不,它不能阻止临时工。您仍然可以通过引用将对象带到构造函数中,但在初始化时会带上它的地址。@BenjaminLindley,不是在这种情况下,但我知道有些业务逻辑对象的复制构造是简单的,但分配是复杂的(不是必需的)。在这种情况下,这根本不是必要的。至于指针-是的,你是对的。@BenjaminLindley我添加了一个使用指针的例子。我甚至没有想到你会希望比较器是可复制的
std::sort(nodes.begin(), nodes.end(), [&g](const Index& lhs, const Index& rhs) {
    return g.getNode(lhs).futureVolume() > g.getNode(rhs).futureVolume();
});