C++20 std::common_reference 的目的是什么?
What is the purpose of C++20 std::common_reference?
C++20 引入了std::common_reference
。它的目的是什么?有人可以举个例子吗?
common_reference
源于我对 STL 迭代器概念化的努力,它包含代理迭代器。
在 STL 中,迭代器有两个特别有趣的关联类型:reference
和 value_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
有代理迭代器。
C++20 引入了std::common_reference
。它的目的是什么?有人可以举个例子吗?
common_reference
源于我对 STL 迭代器概念化的努力,它包含代理迭代器。
在 STL 中,迭代器有两个特别有趣的关联类型:reference
和 value_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
有代理迭代器。