什么是透明比较器?

在 C + + 14中,关联容器似乎已经从 C + + 11-[ sociative.reqmts ]/13发生了变化:

除非 Compare::is_transparent类型存在,否则成员函数模板 findcountlower_boundupper_boundequal_range不应参与过载分辨率。

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

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等成员?

31170 次浏览

在 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<()并声称是透明的。这至少对于重载小于以 char const*为参数的操作符的 std::string是有效的。由于这些函数对象也是新的,即使它们做了错误的事情(例如需要对某种类型进行转换) ,至少不会是导致性能下降的无声更改。

以下是从 N3657复制的所有意大利面。

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

关联容器查找函数(find, 只接受 key _ type 参数,要求 用户构造(隐式或显式) Key _ type 来执行查找 当比较器只起作用时,在一个集合中搜索大的对象 观察对象的一个领域。用户有强烈的欲望 能够使用其他类型进行搜索,这些类型与 Key _ type.

问: 这能解决什么问题

A. 工作组对以下代码表示关切:

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

在 C + + 11中,这将构造一个单独的 std: : string 临时文件,然后 将其与元素进行比较以找到密钥。

With the change proposed by N3465 the std::set::find() function would 是一个无约束的模板,它将传递 const char * 到比较器函数 std: : less,这将 为每次比较构造一个 std: : string 临时值 认为这个性能问题是一个严重的问题 模板 find ()函数也会阻止在 指针的容器,这将导致以前有效的代码不再 编译,但这被认为是一个不太严重的问题比沉默 业绩回归

问: does this change how standard containers work

这项建议修改了《税务条例》的关联容器 通过使用成员函数重载查找成员函数 templates. There are no language changes.

问: 默认集也会丢失它的 find、 count 等成员

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

引用 哎呀的话,

在 C + + 14中,std: : set: : find 是一个模板函数,如果 Is _ arency 存在。您传入的类型不需要 是关键,只是相当于你的比较器。

和 n3657,

在23.2.4[ sociative.reqmts ]中添加第13段: 成员函数模板查找、下限、上限和 不得参与过载分辨率,除非 类型 Compare: : is _ arency 根本不存在确实存在。

N3421 提供了 “透明算子函子”的一个例子。

The 完整代码在这里.

这能解决什么问题,

See Dietmar 的回答 and Remyabel 的回答.

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

No, not by default.

find等的新成员函数模板重载允许您使用与容器的键相当的类型,而不是使用键类型本身。请参阅 Joaquín Ma López Muñoz 的 N3465,了解增加这一特性的理由和详细的、精心编写的建议。

在布里斯托尔的会议上,LWG 同意异构查找功能是有用和可取的,但我们不能确定 Joaquin 的建议在所有情况下都是安全的。N3465提案会给一些程序带来严重的问题(参见 对现有代码的影响部分)。Joaquín 准备了一份更新的提案草案,其中包括一些具有不同权衡的替代实施方案,这对于帮助 LWG 理解利弊非常有用,但它们都有可能以某种方式破坏某些方案,因此没有就增加这一功能达成共识。我们决定,尽管无条件地添加这个特性不安全,但是如果默认禁用它并且只“选择加入”,那么它就是安全的。

N3657提案的关键区别(这是我和 STL 在 N3465基础上最后一分钟的修订,后来 Joaquín 未发表的草案)是添加 is_transparent类型作为可用于选择新功能的协议。

如果不使用“透明函数”(即定义 is_transparent类型的函数) ,那么容器的行为和以前一样,这仍然是默认的。

如果您选择使用 std::less<>(对于 C + + 14来说是新的)或另一种“透明函数”类型,那么您将获得新的功能。

使用别名模板很容易:

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

名称 is_transparent来自 STL 的 is_transparent2,它在 C + + 14中添加了“菱形操作符”。“透明函数”是一种接受任何参数类型(不一定相同)并简单地将这些参数转发给另一个操作符的函数。这样的函数恰好就是你想要的异构查找关联容器,所以类型 is_transparent被添加到所有的菱形操作符中,并作为标记类型来指示应该在关联容器中启用新的功能。从技术上讲,容器不需要“透明函数”,只需要一个支持异构类型调用它的函数(例如,根据 STL 的定义,is_transparent3中的 pointer_comp类型不透明,但定义 pointer_comp::is_transparent允许使用它来解决问题)。如果你只在 std::set<T, C>中查找 Tint类型的键,那么 C只需要调用 Tint类型的参数(两种顺序) ,它不需要真正透明。我们使用这个名字部分是因为我们想不出一个更好的名字(我更喜欢 is_transparent0,因为这样的函数使用静态多态性,但是已经有一个 is_transparent1类型 trait,它指的是动态多态性)。

Stephan T Lavavej 谈到了编译器不断创建临时函数的问题,以及他提出的透明操作符函数将如何在 c + + 1y 中解决这个问题

GoingNative2013-Don’t help the Compiler (at about the hour mark)