当前位置: 首页 > article >正文

C++ ADL参数依赖查找

自以为作为一个C++老鸟,对C++里面各种概念应该都比较熟悉了,但是今天看书的时候又学到了一个装逼的概念ADL,本着学C++装逼装到底的精神,就把这个概念学习了一番。

ADL 的工作原理

在C++中,ADLArgument-Dependent Lookup 的缩写即参数依赖查找。它是一种在特定情况下用来查找函数或运算符的规则。

ADL 会在调用函数时,除了按照通常的作用域规则查找函数外,还会根据函数参数的命名空间类类型来查找可能的候选函数。

当你调用一个未限定作用域的函数(例如没有写 namespace:: 的函数调用),编译器会:

  • 先在调用处的普通作用域中查找函数(即通过标准的名称查找规则)。
  • 如果未找到匹配的函数,编译器会根据参数类型的命名空间类所在的命名空间,继续查找函数。
  • 编译器会优先选择参数所在命名空间中的函数,而不是全局命名空间中的同名函数。
#include <iostream>

namespace MyNamespace {
    struct MyStruct {};

    void print(const MyStruct&) {
        std::cout << "MyNamespace::print called\n";
    }
}

int main() {
    MyNamespace::MyStruct obj;

    // 调用未限定作用域的函数
    print(obj); // ADL 会查找到 MyNamespace::print
    return 0;
}

在上面的例子中,虽然没有显式写出 MyNamespace::print(obj),但由于参数 objMyNamespace::MyStruct 类型,ADL 会将 MyNamespace 纳入函数查找范围,最终找到 MyNamespace::print

ADL 与运算符重载

其实在最常见的运算符重载中已经有ADL的使用,只不过之前不知道ADL这个概念而已,看下面例子是不是很熟悉

#include <iostream>

namespace Math {
    struct Vector {
        int x, y;
    };

    // 定义一个和全局 operator<< 不冲突的版本
    std::ostream& operator<<(std::ostream& os, const Vector& v) {
        os << "(" << v.x << ", " << v.y << ")";
        return os;
    }
}

int main() {
    Math::Vector v{3, 4};

    // ADL 确保 operator<< 从 Math 命名空间查找到正确的定义
    std::cout << v << std::endl;

    return 0;
}

std::ostream 重载的 << 流运算符是定义在标准命名空间 std 中的,上面函数本该调用 std::operator<<(std::cout, v),但是在 Math 命名空间中,用户重载了Math::Vectoroperator<< 定义,ADL 会自动找到正确的 Math::operator<<

运算符重载是 ADL 的一个经典应用场景,因为运算符通常与自定义类型的命名空间相关联,ADL 可以确保运算符能正确地从参数的相关命名空间中找到。

ADL 和 std::swap

std::swap 是 C++ 标准库中的一个函数模板,用于交换两个对象的值。为了支持自定义类型,可以在自定义类型的命名空间中重载 swap

#include <algorithm> // std::swap
#include <iostream>

namespace Custom {
    struct Widget {
    int value;
};

    // 定义命名空间范围的自定义 swap
    void swap(Widget& lhs, Widget& rhs) {
        std::swap(lhs.value, rhs.value); // 使用标准库的 swap 交换内部值
        std::cout << "Custom::swap called\n";
    }
}

int main() {
    Custom::Widget w1{10}, w2{20};

    // 调用 std::swap
    using std::swap;
    swap(w1, w2); // ADL 会查找到 Custom::swap

    std::cout << "w1.value = " << w1.value << ", w2.value = " << w2.value << "\n";

    return 0;
}

调用 swap(w1, w2) 时,标准库的 std::swap 不适合直接处理 Custom::Widget。ADL 将根据参数 w1w2 的类型,进入 Custom 命名空间,找到 Custom::swap

ADL 和隐藏友元函数

隐藏友元函数是指通过在类中定义友元函数,但将其声明和定义放在类的内部,而不是类的外部。这种方式使得友元函数无法直接在类外部被普通的名称查找规则找到,但它可以通过 ADL 被正确查找到。隐藏友元函数的作用通常是防止全局作用域污染,限制函数的可见性,使得函数仅在需要时通过 ADL 查找到。

#include <iostream>

class MyClass {
public:
    MyClass(int value) : value_(value) {}

    // 声明一个友元函数
    friend std::ostream& operator<<(std::ostream& os, const MyClass& obj) {
        os << "MyClass(" << obj.value_ << ")";
        return os;
    }

private:
    int value_;
};

int main() {
    MyClass obj(42);

    // ADL 会查找到友元函数 operator<<
    std::cout << obj << std::endl;

    // std::operator<< 不会因为友元函数的定义而被隐藏
    std::cout << "Test" << std::endl;
    return 0;
}
  • operator<< 是一个隐藏友元函数,因为它的声明和定义都在 MyClass 内部。
  • 当调用 std::cout << obj 时,ADL 会通过参数 obj 的类型(MyClass)进入 MyClass 的定义范围,并找到 operator<<
总结

上面的一些示例代码其实是日常中比较常见的稀疏平常的代码,但是之前基本上不知道里面还有ADL这一说法,相信大家了解ADL概念了,应该对之前一些稀松平常的代码有更深的理解。


http://www.kler.cn/a/413060.html

相关文章:

  • 【k8s深入学习之 Scheme】全面理解 Scheme 的注册机制、内外部版本、自动转换函数、默认填充函数、Options等机制
  • insmod一个ko提供基础函数供后insmod的ko使用的方法
  • 若依框架部署在网站一个子目录下(/admin)问题(
  • python爬虫安装教程
  • Apache OFBiz xmlrpc XXE漏洞(CVE-2018-8033)
  • Spring Boot 2 和 Spring Boot 3 中使用 Spring Security 的区别
  • scala统计词频
  • 嵌入式工程师面试笔试总结——day2
  • TorchMoji使用教程/环境配置(2024)
  • 记录下在html文件中如何直接使用npm依赖,以threejs为例
  • sentry前端接入 报错403
  • 2022 年 3 月青少年软编等考 C 语言三级真题解析
  • YourPHPCMS Register_checkEmail存在sql注入漏洞
  • uniapp中的事件:v-on
  • Spring Boot 3 集成 Spring Security(3)数据管理
  • 同时多平台git配置:GitHub和Gitee生成不同的SSH Key
  • WPF——自定义ToolTip
  • Git远程仓库过大导致clone失败的解决方法
  • pytorch 和tensorflow loss.item()` 只能用于只有一个元素的张量. 防止显存爆炸
  • 什么是缓存击穿?如何避免之布隆过滤器
  • 07 初始 Oracle 优化器
  • Java设计模式笔记(一)
  • 14、保存与加载PyTorch训练的模型和超参数
  • PyTorch:神经网络的基本骨架 nn.Module的使用
  • HBase运维需要掌握的技能(1)
  • 关于在矩阵中枚举点的 dp