提问人:Kerrek SB 提问时间:12/2/2013 最后编辑:Alexis WilkeKerrek SB 更新时间:11/19/2022 访问量:36186
什么是透明比较器?
What are transparent comparators?
问:
在 C++14 中,关联容器似乎从 C++11 发生了变化 – [associative.reqmts]/13 说:
除非类型存在,否则成员函数模板 、 、 和 不应参与重载解析。
find
count
lower_bound
upper_bound
equal_range
Compare::is_transparent
使比较器“透明”的目的是什么?
C++14 还提供了如下库模板:
template <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;
};
例如,不会有一个透明的比较器,但会有一个。std::set<T, std::less<T>>
std::set<T, std::less<>>
这解决了什么问题,这是否会改变标准容器的工作方式?例如,的模板参数仍然是 ,那么默认集会丢失其 、 等成员吗?std::set
Key, Compare = std::less<Key>, ...
find
count
答:
在 C++11 中没有成员模板等。也就是说,这种变化不会丢失任何内容。成员模板是在 n3657 中引入的,以允许将异构密钥与关联容器一起使用。我没有看到任何具体的例子,除了这个好例子和坏例子!find()
lower_bound()
该用途旨在避免不必要的转换。如果成员模板不受约束,则现有代码可以直接传递对象,这些对象在没有成员模板的情况下会进行转换。n3657 中的示例用例是使用字符串文本在 中定位对象:使用 C++11 定义,在将字符串文本传递给相应的成员函数时构造对象。通过更改,可以直接使用字符串文字。如果底层比较函数对象仅根据这一点实现,那就不好了,因为现在将为每个比较创建一个。另一方面,如果底层比较函数对象可以采用 a 和 string 文字,则可以避免构造临时对象。is_transparent
std::set<std::string>
std::string
std::string
std::string
std::string
比较函数对象中的嵌套类型提供了一种指定是否应使用模板化成员函数的方法:如果比较函数对象可以处理异类参数,则定义此类型以指示它可以有效地处理不同的参数。例如,新的运算符函数对象只是委托给并声明是透明的。至少,这比作为参数的操作员过载要少。由于这些函数对象也是新的,即使它们做了错误的事情(即需要对某种类型进行转换),它至少不会是导致性能下降的静默更改。is_transparent
operator<()
std::string
char const*
评论
is_transparent
std::less<Key>
std::less<...>
is_transparent
std::less<void>
is_transparent
以下是 n3657 的所有复制粘贴。
问:使比较器“透明”的目的是什么?
一个。关联容器查找函数(find、lower_bound、 upper_bound,equal_range)只接受key_type的论点,要求 用户构造(隐式或显式)对象 key_type进行查找。这可能很昂贵,例如构建一个 大对象在集合中搜索时仅比较器功能 查看对象的一个字段。用户有强烈的愿望 能够使用与 key_type。
问:这解决了什么问题
一个。LWG 对以下代码表示担忧:
std::set<std::string> s = /* ... */; s.find("key");
在 C++11 中,这将构造一个临时的 std::string,然后 将其与元素进行比较以找到键。
根据 N3465 提出的更改,std::set::find() 函数将 是一个不受约束的模板,它将传递 const char* 到比较器函数 std::less,这将 为每次比较构造一个临时的 std::string。LWG的 认为此性能问题是一个严重的问题。这 template find() 函数还可以防止在 指针容器,这会导致以前有效的代码不再有效 编译,但这被视为一个不如沉默那么严重的问题 性能回归
问:这是否会改变标准容器的工作方式
一个。此提案修改了 和 中的关联容器 通过使用成员函数重载查找成员函数 模板。没有语言变化。
问:默认集也会丢失其 find、count 等成员
答:几乎所有现有的 C++11 代码都不受影响,因为该成员 除非使用新的 C++14 库功能,否则函数不存在 作为比较函数。
引用 Yakk 的话,
在 C++14 中,std::set::find 是一个模板函数,如果 Compare::is_transparent 存在。您传入的类型不需要 是 Key,等同于你的比较器。
和 n3657,
在23.2.4 [associative.reqmts]中增加第13段: 成员函数模板查找、lower_bound、upper_bound和 equal_range不得参与过载解决,除非 类型 Compare::is_transparent
不存在确实存在。
评论
std::set<std::string>
char const *
std::set<std::string, std::less<>>
With the change proposed by N3465 the std::set::find() function would be an unconstrained template which would pass the const char* through to the comparator function, std::less<std::string>, which would construct a std::string temporary for every comparison. The LWG considered this performance problem to be a serious issue.
这解决了什么问题,
参见 Dietmar 的回答和 remyabel 的回答。
这是否会改变标准容器的工作方式?
不,不是默认的。
etc. 的新成员函数模板重载允许您使用与容器的键相当的类型,而不是使用键类型本身。请参阅 Joaquín Mª López Muñoz 的 N3465,了解添加此功能的基本原理和详细、仔细编写的提案。find
在布里斯托尔会议上,LWG 一致认为异构查找功能是有用且可取的,但我们不能确定 Joaquín 的提议在所有情况下都是安全的。N3465 提案会给某些程序带来严重问题(请参阅对现有代码的影响部分)。Joaquín 准备了一份更新的提案草案,其中包含一些具有不同权衡的替代实现,这对于帮助 LWG 了解利弊非常有用,但他们都冒着以某种方式破坏某些程序的风险,因此没有就添加该功能达成共识。我们认为,尽管无条件添加该功能并不安全,但如果默认情况下禁用该功能并且仅“选择加入”,则该功能将是安全的。
N3657 提案(这是我和 STL 基于 N3465 和 Joaquín 后来未发表的草案的最后一刻修订版)的主要区别在于将类型添加为可用于选择加入新功能的协议。is_transparent
如果不使用“透明函子”(即定义类型的函子),则容器的行为与它们一直以来的行为相同,这仍然是默认设置。is_transparent
如果您选择使用(这是 C++14 的新功能)或其他“透明函子”类型,那么您将获得新功能。std::less<>
使用别名模板很容易:std::less<>
template<typename T, typename Cmp = std::less<>, typename Alloc = std::allocator<T>>
using set = std::set<T, Cmp, Alloc>;
这个名字来自STL的N3421,它在C++14中添加了“菱形运算符”。“透明函子”是接受任何参数类型(不必相同)并简单地将这些参数转发给另一个运算符的函数。这样的函子恰好是您在关联容器中进行异构查找所需的内容,因此该类型已添加到所有菱形运算符中,并用作标记类型,以指示应在关联容器中启用新功能。从技术上讲,容器不需要“透明函子”,只需要一个支持使用异构类型调用它的函子(例如,根据 STL 的定义,https://stackoverflow.com/a/18940595/981959 中的类型不是透明的,但定义允许使用它来解决问题)。如果您只使用 type 的键在 you 中查找,或者只需要使用 type 和 的参数(按任一顺序)调用,则它不需要真正透明。我们之所以使用这个名字,部分原因是我们想不出一个更好的名字(我更愿意,因为这样的函子使用静态多态性,但已经有一个类型特征指的是动态多态性)。is_transparent
is_transparent
pointer_comp
pointer_comp::is_transparent
std::set<T, C>
T
int
C
T
int
is_polymorphic
std::is_polymorphic
评论
<>
<>
Stephan T Lavavej 谈到了编译器不断创建临时函数的问题,以及他提出的透明运算符函子将如何在 c++1y 中解决这个问题
评论