Ежедневный бит (е) C++ # 223, Реализация точки настройки с использованием концепций ADL и C++ 20.

Одна из возможностей ввести точку настройки в библиотеку — через ADL (поиск, зависящий от аргумента). С появлением концепций C++20 этот подход стал намного чище.

Niebloid в сочетании с концепцией, обнаруживающей наличие пользовательской реализации, может справиться с откатом к реализации по умолчанию без необходимости возиться с пространствами имен на вызывающем сайте.

#include <utility>

namespace dflt {
namespace impl {

// Concept checking whether an ADL call is valid.
template <typename T> 
concept HasADL = requires(T a) { do_something(a); };

// Main machinery to pick between default or ADL implementation.
struct DoSomethingFn {
    // Type has a custom implementation, simply call, relying on ADL.
    template <typename T> void operator()(T&& arg) const
    requires HasADL<T> { do_something(std::forward<T>(arg)); }

    // Type doesn't have a custom implementation, use the default.
    template <typename T> void operator()(T&&) const
    requires (!HasADL<T>) { /* default implementation */ }
};    
}
// Inline namespace makes the inline variable not conflict with 
// implementations for types in dflt namespace.
inline namespace var {
// Only invocable as dflt::do_something, not visible to ADL.
constexpr inline auto do_something = impl::DoSomethingFn{};
}
}

namespace lib { struct X { friend void do_something(const X&){}; }; }
namespace dflt { struct Y { friend void do_something(const Y&){}; }; }

int main() {
    int a = 0; lib::X x; dflt::Y y;
    // No explicit implementation for int, falls back to default.
    dflt::do_something(a);
    // Calls the friend function do_something for X
    dflt::do_something(x);
    // Calls the friend function do_something for Y
    dflt::do_something(y);

    // Pure-ADL calls, will only invoke custom implementations.
    do_something(x);
    do_something(y);
}

Откройте пример в Compiler Explorer.