0
点赞
收藏
分享

微信扫一扫

C++ ADL & CPO介绍

简单聊育儿 2023-03-11 阅读 99

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/​​

举报

相关推荐

0 条评论