什么是自定义点对象以及如何使用它们? C++标准的最后一稿介绍了所谓的“定制点对象”() 范围库广泛使用的
我似乎明白,它们提供了一种编写自定义版本的什么是自定义点对象以及如何使用它们? C++标准的最后一稿介绍了所谓的“定制点对象”() 范围库广泛使用的,c++,c++20,C++,C++20,我似乎明白,它们提供了一种编写自定义版本的开始、交换、数据等的方法,这些都是 由ADL的标准库找到。对吗 这与以前的做法有什么不同?以前的做法是,用户为自己的类型定义重载,例如,begin 名称空间?特别是,为什么它们是对象 什么是自定义点对象 它们是命名空间std中的函数对象实例,实现了两个目标:首先无条件触发(概念化)参数的类型要求,然后分派到命名空间std中的正确函数或通过ADL 特别是,为什么它们是对象 这对于绕过第二个查找阶段是必要的,该阶段将通过ADL直接引入用户提供的功能(这应该由
开始
、交换
、数据
等的方法,这些都是
由ADL的标准库找到。对吗
这与以前的做法有什么不同?以前的做法是,用户为自己的类型定义重载,例如,begin
名称空间?特别是,为什么它们是对象
什么是自定义点对象
它们是命名空间std
中的函数对象实例,实现了两个目标:首先无条件触发(概念化)参数的类型要求,然后分派到命名空间std
中的正确函数或通过ADL
特别是,为什么它们是对象
这对于绕过第二个查找阶段是必要的,该阶段将通过ADL直接引入用户提供的功能(这应该由设计推迟)。有关更多详细信息,请参见下文
。。。如何使用它们
在开发应用程序时:您基本上不需要。这是一个标准的库功能,它将为将来的定制点添加概念检查,希望在模板实例化出错时产生清晰的错误消息。但是,通过对此类定制点的限定调用,您可以直接使用它。下面是一个虚构的std::customization\u point
对象的示例,该对象依附于设计:
namespace a {
struct A {};
// Knows what to do with the argument, but doesn't check type requirements:
void customization_point(const A&);
}
// Does concept checking, then calls a::customization_point via ADL:
std::customization_point(a::A{});
这在当前的情况下是不可能的,例如std::swap
,std::begin
等
解释(概述)
让我试着理解一下标准中这一部分背后的建议。标准库使用的“经典”定制点存在两个问题
- 他们很容易出错。例如,在通用代码中交换对象应该是这样的
首先,对例如template<class T> void f(T& t1, T& t2) { using std::swap; swap(t1, t2); }
的限定调用始终通过std::begin(someObject)
std::\uuu detail::\uuu begin\fn绕行, 这是需要的。对于一个不合格的电话,我再次参考原始文件: 在将
引入范围后调用begin的情况下,情况 这是不同的。在查找的第一阶段,名称begin将解析为全局对象std::begin
。由于查找找到的是对象而不是函数,因此查找的第二阶段不是 表演。换句话说,如果std::begin
是一个对象,那么std::begin
使用std::begin;开始(a)代码>是 相当于
std::begin(a)代码>我们已经看到,它在 代表用户 这样,可以在
命名空间中的函数对象内执行概念检查, 在执行对用户提供功能的ADL调用之前。这是无法避免的。“自定义点对象”有点用词不当。许多——可能是大多数——实际上并不是定制点 像std
、ranges::begin
、和ranges::end
等都是“真正的”CPO。调用其中一个会导致进行一些复杂的元编程,以确定是否有有效的自定义ranges::swap
或begin
或end
进行调用,或者是否应该使用默认实现,或者调用是否应该是格式错误的(以SFINAE友好的方式)。由于许多库概念是根据有效的CPO调用定义的(如swap
和范围
),因此正确约束的泛型代码必须使用此类CPO。当然,如果您知道具体类型以及从中获取迭代器的另一种方法,请放心 像可交换
这样的东西是没有“CP”部分的CPO。他们总是做默认的事情,所以这不是什么定制点。类似地,范围适配器对象是CPO,但它们没有任何可定制的特性。将它们分类为CPO更多的是一致性(对于ranges::cbegin
)或规范便利性(适配器)cbegin
最后,像ranges::所有的
都是准cpo或niebloid。它们被指定为函数模板,具有特殊的神奇ADL阻塞属性和weasel措辞,以允许它们作为函数对象实现。这主要是为了防止当
中的受约束算法被称为unqualified时,ADL在命名空间std::ranges
中拾取未受约束的重载。因为std
算法接受迭代器哨兵对,所以它通常没有其std::ranges
对应的算法那么专业化,因此会丢失重载解析。它不应该是std
在匿名名称空间中?请注意,ODR的诡计是由C++17内联变量实现的。现在constepr auto begin=\uu detail::\uu begin\u fn{}
内联constexpr\uuuuu详细信息::\uuuu begin\fn begin{}代码>应该足够了。这是标准库的一个实现细节还是用户必须注意自定义点?在
中没有直接的CPO,IIRC。@Peregring lk我也这么认为,否则会破坏向后兼容性。std::
,ranges::data
,和ranges::size
?它们是“真实的”CPO吗?是的,它们实际上是可定制的。范围::空
namespace std { namespace __detail { /* Classical definitions of function templates "begin" for raw arrays and ranges... */ struct __begin_fn { /* Call operator template that performs concept checking and * invokes begin(arg). This is the heart of the technique. * Everyting from above is already in the __detail scope, but * ADL is triggered, too. */ }; } /* Thanks to @cpplearner for pointing out that the global function object will be an inline variable: */ inline constexpr __detail::__begin_fn begin{}; }