云计算百科
云计算领域专业知识百科平台

深入理解C++ 重载 <二> 重载解析:编译器如何选择函数

重载解析(overload resolution)是 C++ 编译器在调用重载函数时,根据实参在编译期选择最合适函数的过程。理解它的关键是掌握三个步骤和转换序列的等级。


1. 重载解析的三个步骤

假设有这样一次调用 f(args…),编译器按顺序执行:

第一步:确定候选函数(candidate functions)

候选函数是名字查找找到的所有同名函数,包括:

  • 同一作用域中声明的同名函数;

  • 通过 using 引入的同名函数;

  • 通过 ADL(实参依赖查找)在实参关联命名空间中找到的同名函数;

  • 函数模板(会先推导模板参数)。

第二步:确定可行函数(viable functions)

可行函数必须满足两个条件:

  • 参数个数匹配(考虑默认参数后);

  • 每个实参都能隐式转换到对应形参类型。

  • 不满足任一条件的函数被淘汰。

    第三步:选择最佳可行函数(best viable function)

    对每个可行函数,为每个实参计算一个隐式转换序列(implicit conversion sequence),然后两两比较。

    规则:

    如果函数 A 对每个参数的转换都不比 B 差,且至少有一个参数的转换比 B 好,则 A 胜出。

    如果找不到这样的函数,就是二义性(ambiguous),编译报错。


    2. 隐式转换序列的等级

    从好到差:

    等级内容例子
    1. 精确匹配 identity、左值到右值、数组到指针、函数到指针、限定转换 int -> int,int[3] -> int*,int -> const int
    2. 提升 整型提升、浮点提升 char -> int,short -> int,float -> double
    3. 标准转换 其他内置类型转换、指针转换、bool 转换 int -> double,double -> int,T* -> void*
    4. 用户定义转换 通过转换构造函数或 operator T() const char* -> std::string
    5. 省略号匹配 f(…)

    注意:提升优于标准转换,这是很多重载解析结果的关键。


    3. 常见特殊规则

    3.1 非模板优先

    普通函数和函数模板都能匹配且转换序列相同时,普通函数优先。

    cpp

    template<class T> void f(T);
    void f(int);

    f(1); // 调用 f(int),非模板优先

    3.2 更特化的模板优先

    两个模板都匹配时,选择更特化的那个。

    cpp

    template<class T> void f(T);
    template<class T> void f(T*);

    int x;
    f(&x); // 调用 f(T*),更特化

    3.3 二义性示例

    cpp

    void f(long);
    void f(double);

    f(1); // int -> long 和 int -> double 都是标准转换,二义性

    3.4 多个参数的比较

    对于多参数函数,必须整体比较:

    cpp

    void f(int, double);
    void f(double, int);

    f(1, 2); // 两个函数各有一个参数更好,二义性

    #include <iostream>
    #include <string>

    // ================= 重载集合 =================
    void f(int x) { std::cout << "f(int) <- " << x << "\\n"; }
    void f(double x) { std::cout << "f(double) <- " << x << "\\n"; }
    void f(char x) { std::cout << "f(char) <- " << x << "\\n"; }
    void f(long x) { std::cout << "f(long) <- " << x << "\\n"; }
    void f(const std::string& s) { std::cout << "f(std::string) <- " << s << "\\n"; }
    void f(int x, int y) { std::cout << "f(int,int) <- " << x << "," << y << "\\n"; }
    void f(int x, double y) { std::cout << "f(int,double) <- " << x << "," << y << "\\n"; }

    int main() {
    std::cout << "— 精确匹配 —\\n";
    f(42); // f(int)
    f(3.14); // f(double)
    f('a'); // f(char)
    f(42L); // f(long)

    std::cout << "\\n— 提升 —\\n";
    f(1.0f); // float -> double,调用 f(double)
    f(short(7)); // short -> int,调用 f(int)

    std::cout << "\\n— 用户定义转换 —\\n";
    f("hello"); // const char* -> std::string,调用 f(std::string)

    std::cout << "\\n— 多参数 —\\n";
    f(1, 2); // f(int,int)
    f(1, 2.0); // f(int,double)
    f(1, 2.0f); // float -> double,调用 f(int,double)
    return 0;
    }

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 深入理解C++ 重载 <二> 重载解析:编译器如何选择函数
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!