将参数传递给比较函数?

36

使用STL的sort算法对vector排序时,我想传入自己的比较函数,并且该函数还需要一个参数。

例如,理想情况下,我希望能够进行本地函数声明,如下:

int main() {
    vector<int> v(100);
    // initialize v with some random values

    int paramA = 4;

    bool comp(int i, int j) {
        // logic uses paramA in some way...
    }

    sort(v.begin(), v.end(), comp);
}

然而,编译器对此进行了投诉。当我尝试像这样做时:

int main() {
    vector<int> v(100);
    // initialize v with some random values

    int paramA = 4;

    struct Local {
        static bool Compare(int i, int j) {
            // logic uses paramA in some way...
        }
    };

    sort(v.begin(), v.end(), Local::Compare);
}
编译器仍然报错:"error: use of parameter from containing function"。我该怎么做?我应该创建一些带有全局比较函数的全局变量吗..?谢谢。
4个回答

36

您无法从局部定义的函数中访问本地变量——C++在其当前形式下不支持闭包。语言的下一个版本C++0x将支持此功能,但语言标准尚未最终确定,并且目前很少有人支持当前的草案标准。

要使这个工作正常,您应该将std::sort的第三个参数更改为对象实例而不是函数。 std::sort的第三个参数可以是任何可调用的东西(即任何x,添加括号如x(y, z)具有语法意义)。 最好的方法是定义一个实现operator()函数的结构体,然后传递该对象的实例:

struct Local {
    Local(int paramA) { this->paramA = paramA; }
    bool operator () (int i, int j) { ... }

    int paramA;
};

sort(v.begin(), v.end(), Local(paramA));

注意,我们必须将paramA存储在结构中,因为否则无法从operator()内部访问它。

1
第三个参数是任何可以使用函数调用语法调用的内容,因此既可以是一个函数,也可以是一个定义了 operator() 的类/结构体。 - Eugen Constantin Dinca
谢谢,它正在工作(除了编译器抱怨除非我将结构声明移动到主函数外部。我以为我们可以在本地声明类和结构体..?) - George41
这应该可以工作。尝试发布一个关于那些抱怨的单独问题。 - Basilevs
使用std::map可以做同样的事情吗?如果可以,将结构体实例传递给映射的语法应该是什么? - tjespe

16

在C++中,你不能在另一个函数内定义自由函数。因此,你的第一个代码片段是不正确的。

sort(v.begin(), v.end(), Local::Compare);

第三个参数必须是一个函数对象。在类内重载()操作符,然后创建函数对象。


在C++0x中,你可以使用lambda表达式

auto comp = [&](int m,int n)-> bool {

        return m<n; //or use paramA in some way
    };

sort(v.begin(), v.end(), comp);

2
在我看来,这是一个比预期的解决方案更好的选择,因为它使用了很好的C++特性(lambda)。 - Anonymous

6

一种可能性是在构造比较器对象时传递参数:

class cmp {
    int param;
public:
    cmp(int p) : param(p) {}

    bool operator()(int i, int j) {
        // logic uses param
    }
};

int main() {
    vector<int> v(100);
    // initialize v with some random values

    int paramA = 4;

    sort(v.begin(), v.end(), cmp(paramA));
}

1

//使用std::bind

//例子

{
vector<int> vecInt{2, 4, 10, 20, 30};
    int i = 4;
    sort(vecInt.begin(), vecInt.end(), std::bind( [](int a, int b, int c)
    {
        return abs(a - c) < abs(b - c);
    }, std::placeholders::_1, std::placeholders::_2, i)
    );
}

1
这个问题在 std::bind(和 lambda 表达式)成为标准的一部分之前就被提出过了。使用捕获 lambda 比绑定非捕获 lambda 更简单。 - Caleth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接