Ежедневный бит (е) 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); }