ADL(Argument dependent lookup)
ADL在cppreference中有详细介绍:
“Argument-dependent lookup, also known as ADL, or Koenig lookup [1\], is the set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators. These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup. ” 【1】
即在非限定函数调用时,会将实参的命名空间添加下搜索范围。通过不同实参类型可以看到ADL的作用,以下为相应测试代码:
// 实参为基础类型时
namespace Lib {
void Process(int){}
}
TEST(ADL, FoundmentalTypes)
{
// Process(1); // not found
}
// 实参为限定命名空间中的结构体类型
namespace Lib {
struct Data {};
void Process(Data) {}
}
namespace Custom {
struct UserData : Lib::Data {};
void Process(UserData) {}
}
TEST(ADL)
{
Custom::UserData u {};
Process(u); // 命名空间Lib和Custom都会添加到搜索范围,再通过函数重载决议选择合适的函数
}
// 实参为枚举类型
namespace Custom {
enum class Date {
MON,
TUS
};
void Process(Date) {}
}
TEST(ADL)
{
Process(Custom::Date::MON); // find Custom::Process
}
// 实参为指针类型
namespace Custom {
struct UserData {};
void Process(UserData *) {}
}
TEST(ADL)
{
Custom::UserData *p = nullptr;
Process(p); // find Custom::Process
}
// 实参为函数类型,则将函数类型中的实参类型和返回值命名空间添加到函数搜索范围
namespace Custom {
struct Data {};
void Process(void(*func)(Data)) {}
void Process(Data(*func)()) {}
}
void SetData(Custom::Data) {}
Custom::Data GetData() { return {}; }
TEST(ADL)
{
Process(SetData);
Process(GetData);
}
namespace Custom {
struct Data {};
void Process(void(Data::*func)()) {}
void Process(int Data::*val) {}
}
TEST(ADL)
{
void(Custom::Data::*func)() = nullptr;
Process(func); // 成员函数指针
int Custom::Data::*val = nullptr;
Process(val); // 成员数据指针
}
namespace Lib {
template<typename T>
struct Data {};
}
namespace Custom {
struct UserData {};
template<typename T>
void Process(T){}
}
TEST(ADL)
{
Lib::Data<Custom::UserData> d;
Process(d);
}
namespace Custom {
struct Data {
friend void Process(Data){} // ADL 能找到完全在类或类模板内定义的友元函数
};
}
TEST(ADL)
{
Custom::Data d;
Process(d);
}
// support until c++20
namespace Custom {
struct Data {};
template<int T>
void Process(Data) {}
}
TEST(ADL)
{
Custom::Data d {};
Process<1>(d); // 对带显式指定模板实参的函数模板调用, 在c++20之前无法通过adl找到函数模板
}
CPO(customization points object)
CPO利用ADL实现定制点的用户自定义修改
customization points — hooks used by generic code that end-users can specialize to customize the behavior for their types. 【2】
当使用三方库但需要用户进行定制实现时,通过ADL实现:
namespace StdLib {
template<typename T>
int Process(T&&) { return 0; }
}
namespace Custom {
struct UserData {};
int Process(UserData) { return 10; }
}
TEST(ADL)
{
Custom::UserData d {};
EXPECT_NE(StdLib::Process(d), 10); // 无法找到用户自定义的实现
// using two step
using namespace StdLib; // step1
EXPECT_EQ(Process(d), 10); // step2, 选择合适函数
}
通过限定名称查找无法找到用户自定义函数,而通过two step则可以根据实参选择正确的函数版本;
而CPO的设计则可以优化上述实现:
namespace StdLib {
namespace Detail {
template<typename T>
int Process(T&&) { return 0; } // default Process
struct ProcessObj {
template<typename T>
auto operator()(T&& t) const -> decltype(Process(std::forward<T>(t)))
{
return Process(std::forward<T>(t));
}
};
}
constexpr Detail::ProcessObj Process {};
}
namespace Custom {
struct UserData {};
int Process(UserData) { return 10; }
}
struct Data {};
TEST(CPO)
{
EXPECT_EQ(StdLib::Process(Custom::UserData {}), 10); // 调用用户自定义实现
EXPECT_EQ(StdLib::Process(Data {}), 0); // 调用默认实现
}
此时Process在StdLib中为一个调用对象,用于用户的自定义实现;
参考资料
【1】https://en.cppreference.com/w/cpp/language/adl
【2】https://ericniebler.com/2014/10/21/customization-point-design-in-c11-and-beyond/