什么是透明比较器?

在C ++ 14中,关联容器似乎已经从C ++ 11变成了 – [associative.reqmts] / 13说:

除非存在Compare::is_transparenttypes,否则成员函数模板findcountlower_boundupper_boundequal_range不应参与重载parsing。

使比较器“透明”的目的是什么?

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>, ... ,那么默认集合是否会丢失findcount等成员?

这个问题解决了什么问题,

看到Dietmar和Remyabel的回答。

这是否会改变标准容器的工作方式?

不,不是默认的。

find等的新成员函数模板重载允许您使用与容器的键相似的types,而不是使用键types本身。 请参阅JoaquínMªLópezMuñoz的N3465提供的基本原理和详细的书面提案,以增加此function。

在布里斯托尔会议上,LWG同意这种异乎寻常的查找function是有用的和可取的,但我们不能确定华金的提案在任何情况下都是安全的。 N3465提案会对某些程序造成严重的问题(请参阅对现有代码影响部分)。 Joaquín准备了一个更新的草案,其中有一些替代的实施方式,这些草案提供了不同的权衡,这对LWG理解利弊是非常有用的,但是它们都有可能以某种方式打破一些程序,所以没有共识来添加这个特性。 我们决定,尽pipe无条件地添加该function并不安全,但如果默认情况下禁用并且只有“join”,它将是安全的。

N3657提案(这是我自己的最后一分钟的修订和基于N3465的 STL以及Joaquín后来未发表的草案)的主要is_transparent是增加了is_transparenttypes作为可以用来select新function的协议。

如果你不使用“透明函子”(即定义一个is_transparenttypes),那么容器的行为与以往一样,并且仍然是默认的。

如果您select使用std::less<> (这是C ++ 14的新增function)或另一个“透明函子”types,那么您将获得新的function。

使用std::less<>很容易使用别名模板:

 template<typename T, typename Cmp = std::less<>, typename Alloc = std::allocator<T>> using set = std::set<T, Cmp, Alloc>; 

名字是is_transparent来自STL的N3421 ,在C ++ 14中增加了“钻石操作员”。 一个“透明函子”是一个接受任何参数types(不必是相同的)并简单地将这些参数转发给另一个运算符的函数。 这样的函数恰好是关联容器中异构查找所需要的,所以typesis_transparent被添加到所有钻石运算符,并用作标记types来指示应该在关联容器中启用新的function。 从技术上讲,容器不需要“透明仿函数”,只需要一个支持异构types调用的透明仿函数(例如https://stackoverflow.com/a/18940595/981959中的;pointer_comptypes根据STL的定义是不透明的,但是定义pointer_comp::is_transparent允许它被用来解决问题)。 如果你只用T或者inttypes的键在你的std::set<T, C>查找,那么C只需要用Tinttypes的参数(以任意顺序)调用,那么不需要真正透明。 我们之所以用这个名字,部分是因为我们不能提出一个更好的名字(我更喜欢is_polymorphic因为这样的函数使用静态多态,但是已经有一个std::is_polymorphictypes的特征指的是dynamic多态)。

在C ++ 11中,不存在成员模板find()lower_bound()等。也就是说,这种改变没有任何东西丢失。 成员模板与n3657一起引入,以允许关联容器使用异构关键字。 我没有看到任何具体的例子,除了这个好的和坏的例子之外,这个例子是有用的。

is_transparent使用是为了避免不必要的转换。 如果成员模板不受约束,则现有代码可能会直接通过对象,而不使用成员模板进行转换。 n3657的示例用例是使用string文本在std::set<std::string>定位一个对象:在C ++ 11定义中, std::string对象是在将string文字传递给对应的成员函数。 随着更改,可以直接使用string文字。 如果底层的比较函数对象是用std::string ,那么这个对象是不好的,因为现在每个比较都会创build一个std::string 。 另一方面,如果底层的比较函数对象可以接受一个std::string和一个string,那么可以避免构造一个临时对象。

比较函数对象中嵌套的is_transparenttypes提供了一种指定是否应该使用模板成员函数的方法:如果比较函数对象可以处理异构参数,则它定义此types以表明它可以有效地处理不同的参数。 例如,新的操作符函数对象只是委托给operator<()并声称是透明的。 至less,它适用于std::string ,它重载了less于以char const*为参数的运算符。 由于这些函数对象也是新的,所以即使它们做了错误的事情(即需要某种types的转换)也至less不会导致性能下降。

以下是来自n3657的所有复制面食。

:使比较器“透明”的目的是什么?

答:关联容器查找函数(find,lower_bound,upper_bound,equal_range)只需要key_type的参数,要求用户构build(隐式或显式)key_type的对象来执行查找。 这可能是昂贵的,例如,当比较器function仅查看对象的一个​​字段时,构build一个大的对象来search一个集合。 用户之间强烈的愿望是能够使用其他types的key_type进行search。

问: 这个问题解决了什么问题

答:LWG关心如下的代码:

 std::set<std::string> s = /* ... */; s.find("key"); 

在C ++ 11中,这将构造一个std :: string临时表,然后将它与元素进行比较以find关键字。

随着N3465提出的更改,std :: set :: find()函数将是一个无约束的模板,它将const char *传递给比较函数std :: less,这将构造一个std :: string临时每一个比较。 LWG认为这个性能问题是一个严重的问题。 模板find()函数也可以防止在指针容器中findNULL,这会导致先前有效的代码不再编译,但是这被认为是一个不那么严重的问题,比静默性能回归

问: 这是否会改变标准容器的工作方式?

答:这个提议通过重载查找成员函数和成员函数模板来修改关联容器。 没有语言变化。

问: 默认集合也会失去其查找,计数等成员

答:几乎所有现有的C ++ 11代码都不受影响,因为除非使用新的C ++ 14库特性作为比较函数,否则成员函数不存在。

引用Yakk ,

在C ++ 14中,如果Compare :: is_transparent存在,则std :: set :: find是一个模板函数。 你传入的types不需要是Key,就等于在你的比较器下。

和n3657,

添加23.2.4中的第13段[associative.reqmts]:除非Compare :: is_transparenttypes不存在 ,成员函数模板find,lower_bound,upper_bound和equal_range不应该参与重载parsing。

n3421提供了一个“透明运算符算子”的例子。

完整的代码在这里 。

Stephan T Lavavej谈到编译器不断创build临时对象的问题,以及他对透明运算符函数的build议如何在c ++ 1y中解决这个问题

GoingNative 2013 – 不帮助编译器 (大约小时)