【发布时间】:2020-03-19 12:49:56
【问题描述】:
C++20 引入了std::common_reference。它的目的是什么?有人可以举个例子吗?
【问题讨论】:
标签: c++ metaprogramming typetraits c++20
C++20 引入了std::common_reference。它的目的是什么?有人可以举个例子吗?
【问题讨论】:
标签: c++ metaprogramming typetraits c++20
common_reference 是我努力提出一个可容纳代理迭代器的 STL 迭代器概念化的结果。
在 STL 中,迭代器有两种特别感兴趣的关联类型:reference 和 value_type。前者是迭代器的operator*的返回类型,value_type是序列元素的(非常量,非引用)类型。
通用算法通常需要做这样的事情:
value_type tmp = *it;
...所以我们知道这两种类型之间一定存在某种关系。对于非代理迭代器,关系很简单:reference 始终为 value_type,可选 const 和引用限定。 Early attempts at defining the InputIterator concept 要求表达式 *it 可以转换为 const value_type &,对于大多数有趣的迭代器来说,这就足够了。
我希望 C++20 中的迭代器比这更强大。例如,考虑zip_iterator 的需求,它以锁步方式迭代两个序列。当您取消引用 zip_iterator 时,您会得到两个迭代器的 reference 类型的临时 pair。因此,zip'ing 一个 vector<int> 和一个 vector<double> 将具有这些关联类型:
zip迭代器的reference:pair<int &, double &>zip迭代器的value_type:pair<int, double>
如您所见,这两种类型之间并没有相互关联,只需添加顶级 cv- 和 ref 限定。然而让这两种类型任意不同感觉是错误的。显然这里有 some 关系。但是,这两种类型的关系是什么?在迭代器上运行的通用算法可以安全地假设这两种类型吗?
C++20 中的答案是,对于任何 有效的迭代器类型,无论是否代理,reference && 和value_type & 类型共享一个公共引用。换句话说,对于某些迭代器 it,有一些类型 CR 使得以下格式正确:
void foo(CR) // CR is the common reference for iterator I
{}
void algo( I it, iter_value_t<I> val )
{
foo(val); // OK, lvalue to value_type convertible to CR
foo(*it); // OK, reference convertible to CR
}
CR 是通用引用。所有算法都可以依赖这种类型存在这一事实,并且可以使用std::common_reference 来计算它。
所以,这就是common_reference 在 C++20 的 STL 中所扮演的角色。通常,除非您正在编写通用算法或代理迭代器,否则您可以放心地忽略它。它在幕后确保您的迭代器履行其合同义务。
编辑:OP 还要求举个例子。这有点做作,但想象一下它是 C++20,给你一个随机访问范围 r,类型为 R,你对此一无所知,而你想要 sort 这个范围。
进一步想象,出于某种原因,您想使用单态比较函数,例如std::less<T>。 (也许您已经对范围进行了类型擦除,并且还需要对比较函数进行类型擦除并将其传递给virtual?再一次,延伸。)std::less<T> 中的T 应该是什么?为此,您将使用common_reference,或根据它实现的助手iter_common_reference_t。
using CR = std::iter_common_reference_t<std::ranges::iterator_t<R>>;
std::ranges::sort(r, std::less<CR>{});
即使r 范围有代理迭代器,也可以保证工作。
【讨论】:
pair<T&,U&> 和 pair<T,U>& 将有一个共同的引用,它只是 pair<T&,U&>。但是,对于std::pair,没有从pair<T,U>& 到pair<T&,U&> 的转换,即使这种转换原则上是合理的。 (顺便说一句,这就是我们在 C++20 中没有 zip 视图的原因。)
pair,而不是可以专门为其目的设计的类型,并根据需要进行适当的隐式转换?
std::pair;任何合适的具有适当转换的类对类型都可以,并且 range-v3 定义了这种类对类型。在委员会上,LEWG 不喜欢在标准库中添加一个几乎但不完全是 std::pair 的类型,无论是否规范,而没有首先对简单地制作 @987654375 的利弊进行尽职调查@工作。
tuple, pair, tomato, to-MAH-to。 pair 有一个很好的功能,您可以使用.first 和.second 访问元素。结构化绑定有助于解决使用 tuples 的一些尴尬问题,但不是全部。