繁体   English   中英

如何为`std :: multiset`提供自定义比较器而不重载`operator()`,`std :: less`,`std :: greater`?

[英]How to provide custom comparator for `std::multiset` without overloading `operator()`, `std::less`, `std::greater`?

我想要一个自定义比较器用于以下代码。 但是,我不允许重载operator()std::lessstd::greater

我尝试使用lambda实现这一点,但gcc不允许我使用auto作为非静态成员。 还有其他方法让这项工作?

#include <iostream>
#include <map>
#include <set>

class Test 
{
public:
    // bool operator () (const int lhs, const int rhs) { // not allowed
    //     return lhs > rhs;
    // };    
    using list = std::multiset<int  /*, Test*/>;
    std::map<const char*, list> scripts;
};

int main() 
{
    Test t;
    t.scripts["Linux"].insert(5);
    t.scripts["Linux"].insert(8);
    t.scripts["Linux"].insert(0);

    for (auto a : t.scripts["Linux"]) {
        std::cout << a << std::endl;
    }

    std::cout << "end";
}

编辑 :使用lambdas

class Test 
{
  public:
    auto compare = [] (const int a, const int b) { return a < b;}
    using list = std::multiset<int, compare>;    //here
    std::map<const char*, list> scripts;
};

错误:

'auto' not allowed in non-static class member
 auto compare = [] (const int a, const int b) { return a < b;}

我想要一个自定义比较器用于以下代码。 但是,我不能重载operator()std::lessstd::greater

我假设你不允许重载Test类的operator() ,但可能是其他类的。 如果是这样,创建一个内部private函数,它重载operator() ,并且可以using list = std::multiset<int, Compare>;这可能是别名的一部分using list = std::multiset<int, Compare>;

class Test
{
private:
    struct Compare
    {
        bool operator()(const int lhs, const int rhs) const /* noexcept */ { return lhs > rhs; }
    };

public:
    using list = std::multiset<int, Compare>;
    std::map<std::string, list> scripts;
};

我尝试使用lambdas实现这些,但gcc不允许我使用auto作为非静态成员。 还有其他方法让这项工作?

更新 :经过一段时间的研究,我发现了一种方法可以使用lambda函数工作

我们的想法是使用std::multisetdecltype自定义lambda compare作为std::map 脚本的键。 除此之外,提供一个包装方法,用于将条目插入CustomMultiList

完整示例代码:( 查看直播

#include <iostream>
#include <string>
#include <map>
#include <set>

// provide a lambda compare
const auto compare = [](int lhs, int rhs) noexcept { return lhs > rhs; };

class Test
{
private:
    // make a std::multi set with custom compare function  
    std::multiset<int, decltype(compare)> dummy{ compare };
    using CustomMultiList = decltype(dummy); // use the type for values of the map 
public:
    std::map<std::string, CustomMultiList> scripts{};
    // warper method to insert the `std::multilist` entries to the corresponding keys
    void emplace(const std::string& key, const int listEntry)
    {
        scripts.try_emplace(key, compare).first->second.emplace(listEntry);
    }
    // getter function for custom `std::multilist`
    const CustomMultiList& getValueOf(const std::string& key) const noexcept
    {
        static CustomMultiList defaultEmptyList{ compare };
        const auto iter = scripts.find(key);
        return iter != scripts.cend() ? iter->second : defaultEmptyList;
    }
};


int main()
{
    Test t{};
    // 1: insert using using wrapper emplace method
    t.emplace(std::string{ "Linux" }, 5);
    t.emplace(std::string{ "Linux" }, 8);
    t.emplace(std::string{ "Linux" }, 0);


    for (const auto a : t.getValueOf(std::string{ "Linux" }))
    {
        std::cout << a << '\n';
    }
    // 2: insert the `CustomMultiList` directly using `std::map::emplace`
    std::multiset<int, decltype(compare)> valueSet{ compare };
    valueSet.insert(1);
    valueSet.insert(8);
    valueSet.insert(5);
    t.scripts.emplace(std::string{ "key2" }, valueSet);

    // 3: since C++20 : use with std::map::operator[]
    t.scripts["Linux"].insert(5);
    t.scripts["Linux"].insert(8);
    t.scripts["Linux"].insert(0);

    return 0;
}

直到 lambda不是默认构造和可复制的 但是, std :: map :: operator []确实将mapped_type重新排列为可复制构造默认构造 因此,只能使用来自C ++ 20的std::map订阅运算符插入scripts映射的值(即std::multiset<int, decltype(/*lambda compare*/)> )。

您可以在构造函数中使用比较函数的函数指针:

main.cpp中

#include <iostream>
#include <set>

using compType=bool(*)(int lhs, int rhs);

bool custom_compare_function(int lhs, int rhs)
{
    return lhs>rhs;
}


using list = std::multiset<int,compType>;
int main() {
    list l(&custom_compare_function);
    l.insert(1);
    l.insert(4);
    l.insert(2);
    for (auto& item: l) std::cout<<item<<std::endl;
}

产生输出

$ g++ main.cpp 
$ ./a.out 
4
2
1

即使您可以按照自己的方式定义lambda,也会出现问题。 看一下multiset声明

template<
    class Key,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<Key>
> class multiset;

注意每个模板参数是一个类型(使用class关键字)。 现在看看你是如何尝试定义列表的:

using list = std::multiset<int, compare>;
                            ^      ^
                          type   value

第一个参数是好的,但第二个参数是不匹配的。 Compare参数需要是一个类型,而不是一个对象。 解决这种情况的一种通用方法是用decltype(compare)替换compare ,但这似乎不是你想要的(加上它对lambda类型有问题)。 您似乎希望默认构造list使用compare而不仅仅是相同类型的默认构造对象。

所以你需要的是一个类,它的默认构造对象以一种给出你想要的顺序的方式实现operator() 由于我们正在处理int ,标准库为此目的有一些现成的类型,即std::lessstd::greater

using list = std::multiset<int, std::greater<int>>;

但是,我不能重载operator(),std :: less,std :: greater。

嗯......这表明示例代码可能过度简化,因为不需要重载。 好吧,我们假设列表属于某种类型,更难以处理,比如说:

class I { /* Internals not important for this example. */ };
using list = std::multiset<I, ???>;

如果允许修改I ,那么最简单的方法可能是为类型I对象定义operator> (或operator< )。 由于std::greater (或std::less )使用此运算符,因此您可以从标准模板获得所需的顺序而不会重载它。

如果你不被允许修改I ,那么我认为你只剩下编写自己的函数对象了 ,因为这是一个lambda不合适的情况。 幸运的是,实现函数对象的类很容易编写; lambdas已经在其他情况下取代了它们,主要是因为lambda语法往往更方便。

struct CompareI {
    bool operator() (const I & lhs, const I & rhs) const { return /* fill this in */; }
};
using list = std::multiset<I, CompareI>;

虽然这定义了operator() ,但它不是重载。 所以它应该满足给你的要求。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM