静态派发

定制点

定制点

定制点(Customization Point)是指用户可以为算法或库提供自定义行为的接口。C++ 中常见的实现方式有:函数重载、ADL 和 C++20 的 Customization Point Object(CPO)。

通过重载和 ADL

考虑一个简单的 swap 实现:

namespace my {
    template <typename T>
    void swap(T& a, T& b) {
        T tmp = std::move(a);
        a = std::move(b);
        b = std::move(tmp);
    }
}

namespace user {
    struct Widget { int* data; };

    void swap(Widget& a, Widget& b) noexcept {
        using std::swap;
        swap(a.data, b.data);
    }
}

user::Widget w1, w2;
my::swap(w1, w2); // 错误:不会找到 user::swap

上面的写法不会找到用户自定义的 swap,因为 my::swap 只在自己的作用域查找。

正确的做法是使用 ADL,把 swap 定义在被操作类型相同的作用域中,并调用时加上 using std::swap;

namespace user {
    struct Widget { int* data; };

    void swap(Widget& a, Widget& b) noexcept {
        using std::swap;
        swap(a.data, b.data);
    }
}

user::Widget w1, w2;
using std::swap;
swap(w1, w2); // ADL 找到 user::swap

什么是定制点

由于 ADL 和重载规则比较复杂,C++ 社区把这类“可以被用户自定义的函数”称为定制点。例如 std::swapstd::beginstd::end 都是标准库中的定制点。

C++20 引入了 Customization Point Object(CPO),通过一个函数对象来统一调用规则,避免 ADL 陷阱。例如 std::ranges::swap 就是一个 CPO。

#include <utility>

user::Widget w1, w2;
std::ranges::swap(w1, w2); // 自动处理 ADL 和默认实现

设计自己的库时,如果允许用户自定义行为,最好把相关函数定义在用户类型所在命名空间,或者使用 CPO 风格。