c – 什么是自定义点对象以及如何使用它们?
作者:互联网
c标准的最后一个草案引入了所谓的“定制点对象”([customization.point.object]),
它们被范围库广泛使用.
我似乎明白,他们提供了一种编写开始,交换,数据等自定义版本的方法
由ADL的标准库发现.那是对的吗?
这与用户如何定义例如过载的先前实践有何不同?从她自己的类型开始
命名空间?特别是,他们为什么反对?
解决方法:
What are customization point objects?
它们是命名空间std中的函数对象实例,它们实现两个目标:首先对参数无条件地触发(设想)类型要求,然后在命名空间std或通过ADL调度到正确的函数.
In particular, why are they objects?
这是绕过第二个查找阶段所必需的,该阶段将通过ADL直接引入用户提供的功能(这应该被设计推迟).请参阅下面的更多细节.
… and how to use them?
开发应用程序时:主要不是.这是一个标准的库功能,它将概念检查添加到未来的定制点,希望如此产生,例如,当您搞乱模板实例化时,在明确的错误消息中.但是,通过对此类自定义点的合格调用,您可以直接使用它.这是一个符合设计的虚构std :: customization_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等.
说明(N4381的摘要)
让我试着消化标准中这一部分背后的提议.标准库使用的“经典”自定义点有两个问题.
>他们很容易出错.例如,在通用代码中交换对象应该看起来像这样
template<class T> void f(T& t1, T& t2)
{
using std::swap;
swap(t1, t2);
}
但是对std :: swap(t1,t2)进行合格调用过于简单 – 用户提供的
交换永远不会被调用(见
N4381,动机和范围)
>更严重的是,没有办法将(受限制的)约束集中在传递给这些用户提供的函数的类型上(这也是为什么这个主题在C 20中获得重要性的原因).再次
从N4381:
Suppose that a future version of
std::begin
requires that its argument model a Range concept.
Adding such a constraint would have no effect on code that usesstd::begin
idiomatically:
using std::begin;
begin(a);
If the call to begin dispatches to a user-defined overload, then the constraint on
std::begin
has been bypassed.
提案中描述的解决方案可以缓解这两个问题
通过类似下面的方法,std :: begin的虚构实现.
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{};
}
首先,打电话给例如std :: begin(someObject)总是绕过std :: __ detail :: __ begin_fn,
这是所希望的.对于不合格的电话会发生什么,我再次参考原始论文:
In the case that begin is called unqualified after bringing
std::begin
into scope, the situation
is different. In the first phase of lookup, the name begin will resolve to the global object
std::begin
. Since lookup has found an object and not a function, the second phase of lookup is not
performed. In other words, ifstd::begin
is an object, thenusing std::begin; begin(a);
is
equivalent tostd::begin(a);
which, as we’ve already seen, does argument-dependent lookup on the
users’ behalf.
这样,概念检查可以在std命名空间中的函数对象中执行,
在ADL调用用户提供的函数之前执行.没有办法绕过这个.