使用is_transparent
来实现用非Key
类型对关联性容器进行查找。
C++ STL提供四种关联性容器(AssociativeContainer - cppreference):set
, multiset
, map
, multimap
。在这些关联性容器中存放自定义类型,需要我们自己定义比较,常用的有以下几种方法
关联容器的默认比较器都是Compare = std::less<T>
,而该函数对象类模版默认调用类上的operator<
,除非重载。根据这个思路,我们有两种办法:
- 在
namespace std
中特化相应的std::less<T>
类模板,在其中实现operator<(const T& lhs, const T& rhs)
。该函数应该有const
限定,否则编译器会警告:warning: the specified comparator type does not provide a const call operator
- 在该类中实现成员函数的
operator<(const T& rhs)
,但是一定要注意该成员函数必须有const
限定符。因为关联容器的Key
类型都带有const
修饰,如果该成员函数没有const
修饰,则无法被const T
类型的对象调用。
特化struct less<T>
类模版:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct Timer;
using TimerPtr = std::unique_ptr<Timer>;
struct Timer
{
uint64_t expiration_;
uint64_t interval_;
};
namespace std
{
template<> struct less<TimerPtr>
{
bool operator()(const TimerPtr& lhs, const TimerPtr& rhs) const
{
return lhs->expiration_ < rhs->expiration_;
}
};
}
int main() {
auto set = std::set<TimerPtr>{};
}
实现const
修饰的成员函数operator<
:1
2
3
4
5
6
7
8
9
10
11
12
13
14struct Timer
{
uint64_t expiration_;
uint64_t interval_;
bool operator<(const Timer& rhs) const
{
return expiration_ < rhs.expiration_;
}
};
int main() {
auto set = std::set<Timer>{};
}
第二种思路是我们自己传入模版参数Compare
,因此我们需要创建函数对象,并重载他的operator()
- 定义一个Compare类,在该类中实现
operator()(const T& lhs, const T& rhs)
,如果operator()
没有const
限定符会收到编译器警告。 - 定义一个generic lambda表达式
[](const auto& lhs, const auto& rhs){}
。因为只有编译器才知道该lambda表达式的类型,因此我们使用decltype(cmp)
将定义好的lambda表达式的类型传给容器。同时该lambda表达式也必须直接传给该容器的构造函数,因为该容器不能帮你构造出该匿名函数对象。默认情况下,由lambda表达式产生的闭包(closure)对象中的operator()
是也是有const
限定符的,除非该lambda被mutable
修饰。
定义一个Compare类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17struct Timer
{
uint64_t expiration_;
uint64_t interval_;
};
struct TimerCompare
{
bool operator()(const Timer& lhs, const Timer& rhs) const
{
return lhs.expiration_ < rhs.expiration_;
}
};
int main() {
auto set = std::set<Timer, TimerCompare>{};
}
定义一个generic lambda1
2
3
4
5
6
7
8
9
10
11struct Timer
{
uint64_t expiration_;
uint64_t interval_;
};
int main() {
auto cmp = [](auto lhs, auto rhs){return lhs.expiration_ < rhs.expiration_;};
auto set = std::set<Timer, decltype(cmp)>{cmp};
}
使用is_transparent
来实现用非Key
类型对关联性容器进行查找。关联性容器都会提供下面这一组成员函数:lower_bound
, equal_range
, upper_bound
。这一组函数接受一个const Key&
类型的参数,用容器内的比较器进行查找。
1 | struct Timer |
在C++14之前,如果我们想要查找这个set
当中所有expiration_
时间小于当前时间的Timer
,我们就必须构造一个临时对象Timer
,给他填上expiration_
,然后传入lower_bound
进行查找。如果临时对象构造的代价太大,或者构造临时对象有副作用,都对我们的查找造成很多不便。C++14当中,引入了一个标签is_tranparent
。如果我们在我们的比较器中定义该标签,并保证该类型和Key
之间的比较是畅通的,我们就可以用非Key
类型进行比较:
1 | struct Timer |
C++14的标准库中还帮我们写了一个标准的,使用完美转发的is_transparent
比较器std::less<void>
。1
2
3
4
5
6
7
8
9
10
11
12template <class T = void> struct less {
constexpr bool operator()(const T& x, const T& y) const;
typedef T first_argument_type;
typedef T second_argument_type;
typedef bool result_type;
};
template <> struct less<void> {
template <class T, class U> auto operator()(T&& t, U&& u) const
-> decltype(std::forward<T>(t) < std::forward<U>(u));
typedef *unspecified* is_transparent;
};
只要自己定义了operator<
,并且两个类型之间定义过operator<
,就可以直接使用这个std::less<void>
,示例如下:
1 |
|
参考
What are transparent comparators - stackoverflow
How do I use comparator with is_transparent type? - stackoverflow
一个Projection Less的实现
is_transparent: How to search a C++ set with another type than its key - fluentcpp.com
Transparent comparator code minimization - stackoverflow