c++标准的最后一稿引入了所谓的“自定义点对象”([customization.point.object]), 被范围库广泛使用。
我似乎理解它们提供了一种编写自定义版本的 begin
、swap
、data
等的方法,它们是
由 ADL 标准库找到。对吗?
这与以前用户定义重载的做法有何不同,例如begin
为她自己输入
命名空间?特别是,为什么它们是对象?
最佳答案
What are customization point objects?
它们是命名空间 std
中的函数对象实例,满足两个目标:首先无条件触发(概念化)参数的类型要求,然后 在命名空间 std
或通过 ADL 分派(dispatch)到正确的函数。
In particular, why are they objects?
这对于规避第二个查找阶段是必要的,该阶段将通过 ADL 直接引入用户提供的功能(这应该在设计上推迟)。详情见下文。
... and how to use them?
在开发应用程序时:您主要不这样做。这是一个标准库功能,它将为 future 的定制点添加概念检查,希望能产生例如当您弄乱模板实例时,会显示清晰的错误消息。但是,通过对此类自定义点的合格调用,您可以直接使用它。下面是一个符合设计的虚构 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)
进行合格调用太简单了 - 用户提供swap
永远不会被调用(参见 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 onstd::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 objectstd::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 调用之前。没有办法规避这一点。
关于c++ - 什么是自定义点对象以及如何使用它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53495848/