在C ++ 14中,关联容器似乎已经从C ++ 11改变了 - [associative.reqmts] / 13说:
除非{{1}类型,否则成员函数模板
find
,count
,lower_bound
,upper_bound
和equal_range
不得参与重载解析存在。
使比较器“透明”的目的是什么?
C ++ 14还提供了这样的库模板:
Compare::is_transparent
例如,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
等成员吗?
答案 0 :(得分:51)
这解决了什么问题,
见Dietmar和remyabel的回答。
这会改变标准容器的工作方式吗?
不,不是默认。
find
等新成员函数模板重载允许您使用与容器键相当的类型,而不是使用键类型本身。请参阅JoaquínMªLópezMuñoz撰写的N3465以及添加此功能的详细,精心编写的提案。
在布里斯托尔会议上,LWG同意异端查找功能是有用且可取的,但我们无法确定Joaquín的提议在所有情况下都是安全的。 N3465提案会对某些程序造成严重问题(请参阅对现有代码的影响部分)。 Joaquín准备了一份更新的草案提案,其中包含一些具有不同权衡的替代实施方案,这对于帮助LWG了解优缺点非常有用,但它们都有可能以某种方式破坏某些程序,因此没有就增加该功能达成共识。我们认为尽管无条件地添加功能是不安全的,但如果默认情况下禁用它并且只是“选择加入”,那将是安全的。
N3657提案(由我自己和STL根据N3465进行的最后一分钟修订以及后来Joaquín未发布的草案)的主要区别是添加is_transparent
键入可用于选择加入新功能的协议。
如果你不使用“透明函子”(即定义is_transparent
类型的那个),那么容器的行为与它们一直以来的行为相同,而且仍然是默认值。
如果您选择使用std::less<>
(这是C ++ 14的新功能)或其他“透明仿函数”类型,那么您将获得新功能。
使用别名模板轻松使用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中。 “透明函子”是接受任何参数类型(不必相同)并简单地将这些参数转发给另一个运算符的函数。这样的仿函数恰好是您想要在关联容器中进行异构查找的内容,因此类型is_transparent
被添加到所有菱形运算符中,并用作标记类型以指示应在关联容器中启用新功能。从技术上讲,容器不需要“透明函子”,只需支持使用异构类型调用它(例如https://stackoverflow.com/a/18940595/981959中的pointer_comp
类型根据STL的定义不透明,但定义{{ 1}}允许它用于解决问题)。如果您只使用类型为pointer_comp::is_transparent
或std::set<T, C>
的密钥在T
中查找,则int
只需要使用C
和{{类型的参数进行调用1}}(以任何顺序),它不需要真正透明。我们使用该名称的部分原因是因为我们无法提出更好的名称(我更喜欢T
因为这些仿函数使用静态多态,但是已经有int
类型特征引用了动态多态性) 。
答案 1 :(得分:30)
在C ++ 11中,没有成员模板find()
,lower_bound()
等等。也就是说,此更改不会丢失任何内容。使用n3657引入了成员模板,以允许异构密钥与关联容器一起使用。除了好的和坏的例子之外,我没有看到任何有用的具体例子!
is_transparent
用途旨在避免不必要的转化。如果成员模板不受约束,则现有代码可以直接传递直接对象,这些对象在没有成员模板的情况下进行转换。来自n3657的示例用例是使用字符串文字在std::set<std::string>
中定位对象:使用C ++ 11定义,在将字符串文字传递给相应的成员函数时构造std::string
对象。通过更改,可以直接使用字符串文字。如果底层比较函数对象仅以std::string
的方式实现,那么现在将为每次比较创建std::string
。另一方面,如果底层比较函数对象可以采用std::string
和字符串文字,则可以避免构造临时对象。
比较函数对象中的嵌套is_transparent
类型提供了一种指定是否应使用模板化成员函数的方法:如果比较函数对象可以处理异构参数,则它定义此类型以指示它可以有效地处理不同的论点。例如,新的运算符函数对象只委托给operator<()
并声称是透明的。这至少适用于std::string
,它比以char const*
为参数的运算符重载更少。由于这些函数对象也是新的,即使它们做错了(即需要某种类型的转换),它至少也不会导致性能下降的静默变化。
答案 2 :(得分:18)
以下是来自n3657的所有copy-pasta。
Q值。 使比较器“透明”的目的是什么?
一个。关联容器查找函数(find,lower_bound, upper_bound,equal_range)只接受key_type的参数,需要 用户构造(隐式或显式)的对象 key_type进行查找。这可能很昂贵,例如建造一个 仅当比较器功能时,在一个集合中搜索的大对象 看着对象的一个字段。用户强烈渴望 能够使用与其相似的其他类型进行搜索 为key_type。
Q值。 这解决了什么问题
一个。 LWG担心代码如下:
std::set<std::string> s = /* ... */; s.find("key");
在C ++ 11中,这将构造一个单独的std :: string临时 将其与元素进行比较以找到密钥。
通过N3465提出的更改,std :: set :: find()函数会 是一个无约束的模板,它将传递const char * 到比较器函数,std :: less,哪个会 为每次比较构造一个std :: string临时文件。 LWG 认为这个性能问题是一个严重的问题。该 模板find()函数也会阻止在a中查找NULL 指针容器,不再导致以前有效的代码 编译,但这被认为是一个比沉默更严重的问题 绩效回归
Q值。 这会改变标准容器的工作方式
一个。该提议修改了和中的关联容器 通过使用成员函数重载查找成员函数 模板。没有语言变化。
Q值。 默认设置也会丢失其查找,计数等成员
一个。几乎所有现有的C ++ 11代码都不受影响,因为该成员 除非使用新的C ++ 14库特性,否则不存在函数 作为比较函数。
引用Yakk,
在C ++ 14中,std :: set :: find是一个模板函数if 比较:: is_transparent存在。您传入的类型不需要 是关键,在你的比较器下等同。
和n3657,
在23.2.4 [associative.reqmts]中添加第13段: 成员函数模板find,lower_bound,upper_bound和 equal_range不应参与重载决策,除非 类型Compare :: is_transparent
不存在确实存在。
答案 3 :(得分:6)
Stephan T Lavavej谈论编译器不断创建临时代码的问题,以及他的透明运算符函数的提议将如何在c ++ 1y中解决这个问题