如何在不重载 operator()、std::less、std::greater 的情况下为 std::multiset 提供自定义比较器?
How to provide custom comparator for `std::multiset` without overloading `operator()`, `std::less`, `std::greater`?
我想要一个用于以下代码的自定义比较器。但是,我不允许重载operator()
、std::less
、std::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";
}
编辑:使用 lambda
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;}
您可以在构造函数中使用比较函数的函数指针:
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
I want a custom comparator for the following code. However, I cannot
overload operator()
, std::less
, std::greater
.
我假设您不允许超载 Test
class 的 operator()
,但可以是其他 class 的超载。如果是这样,创建一个内部 private
仿函数重载 operator()
并且它可能是别名 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;
};
I tried to achieve these using lambdas but gcc won't allow me to use
auto as a non-static member. Any other way to make this work?
更新: 经过一段时间的研究,我找到了一个可行的方法† 使用 lambda 函数。
想法是使用 std::multiset
的 decltype
和 custom lambda compare 作为 std::map
[=41= 的键]脚本。除此之外,提供用于将条目插入 CustomMultiList
.
的包装器方法
完整示例代码:(See live)
#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;
}
† 直到 c++20 lambda are not default constructable and copyable. But, the std::map::operator[] does requered the mapped_type to be copy constructible and default constructible。因此,使用 std::map
的订阅运算符插入到 scripts
映射的值(即到 std::multiset<int, decltype(/*lambda compare*/)>
)只能从 C++20 开始。
即使您可以按照自己的方式定义 lambda,您的方法也有问题。看看 the multiset
declaration:
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
参数需要是类型,而不是对象。解决这种情况的一种通用方法是将 compare
替换为 decltype(compare)
,但这似乎不是您想要的(而且它对 lambda 类型有问题)。您似乎希望默认构造的 list
使用 compare
而不仅仅是相同类型的默认构造对象。
所以您需要的是 class,其默认构造对象以提供您想要的顺序的方式实现 operator()
。由于我们正在处理 int
,因此标准库有一些用于此目的的 ready-made 类型,即 std::less
和 std::greater
.
using list = std::multiset<int, std::greater<int>>;
However, I cannot overload operator(), std::less, std::greater.
嗯...这表明示例代码可能是 over-simplified,因为不需要重载。好的,假设列表是某种更难处理的类型,比如:
class I { /* Internals not important for this example. */ };
using list = std::multiset<I, ???>;
如果允许修改 I
,那么最简单的方法可能是为类型 I
的对象定义 operator>
(或 operator<
)。由于 std::greater
(或 std::less
)使用此运算符,您可以从标准模板中获得所需的顺序而无需重载它。
如果不允许您修改 I
,那么我认为您只能自己编写 function object,因为这是 lambda 不合适的一种情况。幸运的是,classes 实现函数对象很容易编写; lambda 在其他情况下取代了它们,主要是因为 lambda 语法更方便。
struct CompareI {
bool operator() (const I & lhs, const I & rhs) const { return /* fill this in */; }
};
using list = std::multiset<I, CompareI>;
虽然这定义了一个 operator()
,但它不是重载。所以它应该满足给你的要求。
我想要一个用于以下代码的自定义比较器。但是,我不允许重载operator()
、std::less
、std::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";
}
编辑:使用 lambda
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;}
您可以在构造函数中使用比较函数的函数指针:
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
I want a custom comparator for the following code. However, I cannot overload
operator()
,std::less
,std::greater
.
我假设您不允许超载 Test
class 的 operator()
,但可以是其他 class 的超载。如果是这样,创建一个内部 private
仿函数重载 operator()
并且它可能是别名 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;
};
I tried to achieve these using lambdas but gcc won't allow me to use auto as a non-static member. Any other way to make this work?
更新: 经过一段时间的研究,我找到了一个可行的方法† 使用 lambda 函数。
想法是使用 std::multiset
的 decltype
和 custom lambda compare 作为 std::map
[=41= 的键]脚本。除此之外,提供用于将条目插入 CustomMultiList
.
完整示例代码:(See live)
#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;
}
† 直到 c++20 lambda are not default constructable and copyable. But, the std::map::operator[] does requered the mapped_type to be copy constructible and default constructible。因此,使用 std::map
的订阅运算符插入到 scripts
映射的值(即到 std::multiset<int, decltype(/*lambda compare*/)>
)只能从 C++20 开始。
即使您可以按照自己的方式定义 lambda,您的方法也有问题。看看 the multiset
declaration:
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
参数需要是类型,而不是对象。解决这种情况的一种通用方法是将 compare
替换为 decltype(compare)
,但这似乎不是您想要的(而且它对 lambda 类型有问题)。您似乎希望默认构造的 list
使用 compare
而不仅仅是相同类型的默认构造对象。
所以您需要的是 class,其默认构造对象以提供您想要的顺序的方式实现 operator()
。由于我们正在处理 int
,因此标准库有一些用于此目的的 ready-made 类型,即 std::less
和 std::greater
.
using list = std::multiset<int, std::greater<int>>;
However, I cannot overload operator(), std::less, std::greater.
嗯...这表明示例代码可能是 over-simplified,因为不需要重载。好的,假设列表是某种更难处理的类型,比如:
class I { /* Internals not important for this example. */ };
using list = std::multiset<I, ???>;
如果允许修改 I
,那么最简单的方法可能是为类型 I
的对象定义 operator>
(或 operator<
)。由于 std::greater
(或 std::less
)使用此运算符,您可以从标准模板中获得所需的顺序而无需重载它。
如果不允许您修改 I
,那么我认为您只能自己编写 function object,因为这是 lambda 不合适的一种情况。幸运的是,classes 实现函数对象很容易编写; lambda 在其他情况下取代了它们,主要是因为 lambda 语法更方便。
struct CompareI {
bool operator() (const I & lhs, const I & rhs) const { return /* fill this in */; }
};
using list = std::multiset<I, CompareI>;
虽然这定义了一个 operator()
,但它不是重载。所以它应该满足给你的要求。