image.png

函数重载:使用相同的函数名定义多个函数,每个函数具有不同的参数列表

  • 不能基于不同的返回类型进行重载 ```cpp

    include

int fun(int val) { return val; }

double fun(int val) { return val; }

int main() { fun(1); }

  1. 这样写会进行报错,我们在15行进行函数调用时,没有使用返回值,这里就产生了歧义,返回值究竟是int类型还是double类型,从而引起报错。
  2. - 函数重载与name mangling
  3. ** 名称查找 **
  4. - 限定查找( qualified lookup )与非限定查找( unqualified lookup
  5. ```cpp
  6. #include <iostream>
  7. void fun()
  8. {
  9. std::cout << "global fun is called" << std::endl;
  10. }
  11. namespace MySpace
  12. {
  13. void fun()
  14. {
  15. std::cout << "MySpace::fun is called" << std::endl;
  16. }
  17. }
  18. int main()
  19. {
  20. ::fun();
  21. MySpace::fun(); // 指定了查找空间即MySpace中
  22. }
  • 非限定查找会进行域的逐级查找——名称隐藏( hiding ) ```cpp

    include

void fun() { std::cout << “global fun is called” << std::endl; }

namespace MySpace { void fun() { std::cout << “MySpace::fun is called” << std::endl; } void g() { fun(); } }

int main() { MySpace::g(); }

  1. 输出结果:
  2. ```cpp
  3. MySpace::fun is called
  • 查找通常只会在已声明的名称集合中进行

  • 实参依赖查找( Argument Dependent Lookup: ADL ) 只对自定义类型生效
    ```cpp

    include

namespace MySpace { struct Str{}; void g(Str x) { std::cout << “successfully called” << std::endl; } }

int main() { MySpace::Str obj; g(obj); }

  1. 这里的obj作为实参,传入函数g( ),从而实现函数的成功调用
  2. **_tricky point:_**
  3. ```cpp
  4. #include <iostream>
  5. template <typename T>
  6. void fun(T x)
  7. {
  8. g(x);
  9. }
  10. namespace MySpace
  11. {
  12. struct Str{};
  13. void g(Str x)
  14. {
  15. std::cout << "successfully called" << std::endl;
  16. }
  17. }
  18. int main()
  19. {
  20. MySpace::Str obj;
  21. fun(obj);
  22. }

涉及到模板的一些问题时,看起来违背了“查找通常只会在已声明的名称集合中进行 ”,但这和编译器对模板的一些处理(如模板实例化)有关,其他情况依然适用

重载解析:在名称查找的基础上进一步选择合适的调用函数
– 过滤不能被调用的版本 (non-viable candidates)
● 参数个数不对
● 无法将实参转换为形参
● 实参不满足形参的限制条件
– 在剩余版本中查找与调用表达式最匹配的版本,匹配级别越低越好(有特殊规则)
● 级别 1 :完美匹配或平凡转换(比如加一个 const )
● 级别 2 : promotion 或 promotion 加平凡转换 (整型提升
● 级别 3 :标准转换 或 标准转换加平凡转换
● 级别 4 :自定义转换 或 自定义转换 加平凡转换 或 自定义转换加标准转换
● 级别 5
:形参为省略号的版本
● 函数包含多个形参时,所选函数的所有形参的匹配级别都要优于或等于其它函数
situation 1

  1. // lvalue i -> int& is better than lvalue int -> const int&
  2. #include <iostream>
  3. void fun(int& val)
  4. {
  5. std::cout << "The first fun is called!" << std::endl;
  6. }
  7. void fun(const int& val)
  8. {
  9. std::cout << "The second fun is called!" << std::endl;
  10. }
  11. int main()
  12. {
  13. int x;
  14. fun(x);
  15. }

编译结果:

  1. The first fun is called!

很明显在函数重载中,主函数成功地调用了第一个fun( )函数,我们可以这样理解:x作为实参传入函数中,第一个左值引用绑定到x上,可以实现对x的读写操作,而第二个只能进行读取操作,编译器因此会选择第一个fun( )函数
situation 2

  1. #include <iostream>
  2. void fun(int val)
  3. {
  4. std::cout << "The first fun is called!" << std::endl;
  5. }
  6. void fun(const int& val)
  7. {
  8. std::cout << "The second fun is called!" << std::endl;
  9. }
  10. int main()
  11. {
  12. int x;
  13. fun(x);
  14. }

这段代码编译失败,根据上面的函数重载规则,第一步过滤没有问题,到第二步,根据匹配级别,两个函数同属于级别1,解析出现问题,因此会报错。
situation 3

  1. #include <iostream>
  2. void fun(int& val)
  3. {
  4. std::cout << "The first fun is called!" << std::endl;
  5. }
  6. void fun(const int& val)
  7. {
  8. std::cout << "The second fun is called!" << std::endl;
  9. }
  10. int main()
  11. {
  12. fun(3);
  13. }

编译结果:

  1. The second fun is called!

这里的3作为右值,而左值引用val不能绑定到右值上
situation 4
函数包含多个形参时,所选函数的所有形参的匹配级别都要优于或等于其它函数

  1. // 错误代码
  2. #include <iostream>
  3. void fun(int val,double y) // 1 3
  4. {
  5. std::cout << "The first fun is called!" << std::endl;
  6. }
  7. void fun(int val,float y) // 1 3
  8. {
  9. std::cout << "The second fun is called!" << std::endl;
  10. }
  11. int main()
  12. {
  13. fun(1,1); // 重载解析出现歧义
  14. }

tricky point:

  1. #include <iostream>
  2. void fun(bool val,float y) // 1 3
  3. {
  4. std::cout << "The first fun is called!" << std::endl;
  5. }
  6. void fun(int val,double y) // 2 1
  7. {
  8. std::cout << "The second fun is called!" << std::endl;
  9. }
  10. int main()
  11. {
  12. fun(true,1.0);
  13. }

编译标准不同,在gcc下可以通过编译输出结果,在clang下直接报错,这里就涉及到比较“奇怪”的点 :按照匹配规则,多参数需要每个参数的级别都优于其他函数,此函数才可以作为函数重载解析的选择,但在gcc的标准下,给出了**warning: **ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:,可见这里它把级别之和作为选择标准,因此调用第二个fun( )函数:

  1. The second fun is called!

更改:

  1. #include <iostream>
  2. void fun(bool val,float y) // 1 3
  3. {
  4. std::cout << "The first fun is called!" << std::endl;
  5. }
  6. void fun(int val,double y) // 1 1
  7. {
  8. std::cout << "The second fun is called!" << std::endl;
  9. }
  10. int main()
  11. {
  12. fun(static_cast<int> (true),1.0);
  13. }

编译结果:

  1. The second fun is called!