C++20 std::common_reference 的目的是什么?

What is the purpose of C++20 std::common_reference?

C++20 引入了std::common_reference。它的目的是什么?有人可以举个例子吗?

common_reference 源于我对 STL 迭代器概念化的努力,它包含代理迭代器。

在 STL 中,迭代器有两个特别有趣的关联类型:referencevalue_type。前者是迭代器的operator*的return类型,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 a vector<int> 和 a vector<double> 将具有以下关联类型:

zip 迭代器的 reference : pair<int &, double &>
zip 迭代器的 value_type: pair<int, double>

如您所见,这两种类型仅通过添加顶级 cv- 和 ref 限定彼此无关。然而让这两种类型任意不同感觉是错误的。显然这里有一些关系。但是,它们之间的关系是什么?在迭代器上运行的通用算法可以安全地假定这两种类型是什么?

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?再一次,延伸。) T 应该在 std::less<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 有代理迭代器。