c++ - 什么是自定义点对象以及如何使用它们?

标签 c++ c++20

c++标准的最后一稿引入了所谓的“自定义点对象”([customization.point.object]), 被范围库广泛使用。

我似乎理解它们提供了一种编写自定义版本的 beginswapdata 等的方法,它们是 由 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::swapstd::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 uses std::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, if std::begin is an object, then using std::begin; begin(a); is equivalent to std::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/

相关文章:

c++ - 解决 lambda 表中的 VS2010 错误?

c++ - 'std::vector' : 'U64' is not a valid template type argument for parameter '_Ty'

c++ - 如果类模板将实例化,则变量模板为真?

c++ - 为什么 `iota(0) | take(0)` 不是 C++20 中的模型 range::sized_range?

c++ - span 是否传播 const?

c++ - 如何通过自定义结构传递数组初始化?

c++ - 如何最好地防止函数关闭文件?

Android JNI C++ 段错误

c++ - 引用传递 VS 值传递

c++ - 实现 std::iter_difference_t