跟我学C++中级篇——重载问题分析之函数重载的匹配说明

一、重载的匹配

在前面的函数重载中其实大家明白了一个最根本的原则就是要保证最佳匹配的原则。就如同按照画像寻找一个人一样,先找特征一模一样的,再找类似点最多的,然后依次递减。从概率上分析,特征越是匹配,基本表明其可能是一个人的可能性要高很多。
函数重载的匹配相对于人来说,特征要相对简单很多。但对于初学者或者说不熟悉的开发者来说,仍然是一个比较难缠的问题。

二、具体的匹配方法

在前文的说明中,对具体匹配的优先级进行了说明即: 精确匹配;提升转换;标准转换;用户定义转换;可变参数匹配。其实举一个简单例子大家就明白了:

//伪代码
void test(int);
void test(double);

//调用
int a;char b;double c;
test(a);
test(b);
test(c);

在上面的代码中,a和c属于精确匹配,b属于提供转换。如果把test的double重载注释,c会调用test(int),这就属于标准转换了。当然,如果有多个同一等级的转换存在,编译器就无法分辨出应该调用哪个,会报出一个“ambiguous overload”错误。
下面就针对函数重载的具体过程进行一下分析说明:

  1. 先确定作用域。需要把整个作用域内的重名的函数查找并确定下来,即局部优于全局;导入同名函数需要通过包空间控制
  2. 确定是否有重载可能。需要判断的是函数名称(签名)相同;参数个数与调用的实参的一致性确定;在同一作用域内或可被查找
  3. 参数数量(含默认值处理,要明确有默认值与无默认值重载引起的冲突)处理及参数类型的转换处理,即支持上述匹配的优先级的实参与形参转换
  4. 处理友元和ADL(避免隐式的ADL),并确保其在同一命令空间内;注意,普通函数的匹配优于模板函数
  5. 处理模板的问题特别是SFINAE等机制引起的重载解析无效,即只有成功推导的模板参数参与重载
  6. 标准转换最高优先级为支持精确类型匹配和修饰符的转换(cv),其优先等级判断原则为:左传到右值;数据到指针;函数到指针以及算术转换。
  7. 提升转换要高于标准转换,即小到大高转换要高于非同系类型的转换。比如float到double要高于int到double的转换。即其在重载中优先级会高
  8. 用户自定义转换的需要处理好自定义转换与隐式转换化的歧义;不允许进行基础类型的转换和双向隐式转换(有可能引发循环定义);模板中的自定义要防止引起推导的冲突异常
  9. 最后处理变参情况,可以认为变参是一种保底方式
  10. 依据上面的作用域、签名、具体的处理(如模板的推导)、参数匹配优先级最终确定最佳的匹配函数并调用

在掌握了上述的具体流程逻辑后,就可以分析和解决在重载中为何遇到了问题。并有的放矢的解决实际的问题。

三、举例说明

下面看一个比较全面的重载函数的例子:

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <type_traits>
#include <concepts>


// 参数不同的重载
void print() {
    std::cout << "call print: args 0!" << std::endl;
}

void print(int value) {
    std::cout << "call print:args 1 " << value << std::endl;
}

void print(int a, int b) {
    std::cout << "call print: args 2" << a << ", " << b << std::endl;
}

// 参数不同类型的重载
void print(const char* value) {
    std::cout << "call print:args const char*: " << value << std::endl;
}
void print(const std::string& value) {
    std::cout << "call print:args const string&: " << value << std::endl;
}

void print(double value) {
    std::cout << "call print:args type double: " << value << std::endl;
}


// cv及引用限定符重载 

class CVDemo {
private:
    std::string str_;
    
public:
    CVDemo(const std::string& n) : str_(n) {}
    
    //const重载
    const std::string& getStr() const {

        std::cout << "getStr() const called ."<< std::endl;
        return str_;
    }
    
    std::string& getStr() {
        std::cout << "getStr() non-const called" << std::endl;
        return str_;
    }
    
    //引用限定符重载
    void control() & {
        std::cout << "call control & => lvalue" << std::endl;
    }
    
    void control() && {
        std::cout << "call control && =>rvalue" << std::endl;
    }
    
    void control() const & {
        std::cout << "call control const & => const lvalue" << std::endl;
    }
};


// 函数模板重载
template<typename T>
void display(T value) {
    std::cout << "call display(T) ,args : " << value << std::endl;
}

template<typename T>
void display(T* ptr) {
    std::cout << "call display(T*): pointer args : " << ptr<<std::endl;
    if (ptr) {
    	std::cout << ", value = " << *ptr<<std::endl;
    }
}

template<>
void display<std::string>(std::string value) {
    std::cout << "call display<string>: specialization  args:" << value << std::endl;
}

// 变参模板重载
template<typename... Args>
void PrintData(Args&&... args) {
    std::cout << "PrintData(Args...): args num: " << sizeof...(Args)  << std::endl;
    ((std::cout << args << " "), ...);
    std::cout << std::endl;
}

void PrintData(const char* msg) {
    std::cout << "PrintData(const char*): C-string:" << msg << std::endl;
}

// 运算符重载
class Complex {
private:
    double real_=0.0;
    double imag_ =0.0;
    
public:
    Complex(double r = 0, double i = 0) : real_(r), imag_(i) {}
    
    // 成员运算符重载
    Complex operator+(const Complex& other) const {
        return Complex(real_ + other.real_, imag_ + other.imag_);
    }
    
    Complex operator-() const {
        return Complex(-real_, -imag_);
    }
    
    // 前缀和后缀++
    Complex& operator++() {
        ++real_;
        return *this;
    }
    
    Complex operator++(int) {
        Complex temp = *this;
        ++real_;
        return temp;
    }
    
    // 转换运算符
    explicit operator double() const {
        return real_;
    }
    
    friend Complex operator+(double value, const Complex& c) ;
    friend std::ostream& operator<<(std::ostream& os, const Complex& c);
};

// 非成员运算符重载
std::ostream& operator<<(std::ostream& os, const Complex& c) {
    os << c.real_ << " + " << c.imag_ << "i";
    return os;
}

Complex operator+(double value, const Complex& c) {
    return Complex(value + c.real_, c.imag_);
}

// 继承中的重载 
class Base {
public:
    //虚函数重载
    virtual void run(int d) {
        std::cout << "call Base::run(int): " << d << std::endl;
    }
    
    virtual void run(double d) {
        std::cout << "call Base::run(double): " << d << std::endl;
    }
    
    void genFunc(int d) {
        std::cout << "Base::genFunc(int): " << d << std::endl;
    }
    
    virtual ~Base() = default;
};

class Derived : public Base {
public:
    // 隐藏基类重载 
    void run(int x) override {
        std::cout << "Derived::run(int): " << x << std::endl;
    }
    
    // 引入基类重载的解决方案
    using Base::run;
    
    void genFunc(const std::string& s) {
        std::cout << "Derived::genFunc(string): " << s << std::endl;
    }
};


//概念的重载 (C++20)
template<typename T>
concept Arith = std::is_Arith_v<T>;

template<typename T>
concept StrSimilar = requires(T t) {
    { std::to_string(t) } -> std::convertible_to<std::string>;
};

template<Arith T>
void conceptsDowith(T value) {
    std::cout << "call concepts (Arith): " << value << std::endl;
}

template<StrSimilar T>
void conceptsDowith(T value) {
    std::cout << "call concepts(StrSimilar): " << std::to_string(value) << std::endl;
}

//完美转发重载
template<typename T>
void dowith(T&& value) {
    std::cout << "  call dowith ,value  : " << value << std::endl;
}
template<typename T>
void forwardDemo(T&& value) {
    std::cout << "call forwardDemo(T&&)" << std::endl;
    dowith(std::forward<T>(value));
}

void forwardDemo(const std::string& value) {
    std::cout << "call forwardDemo(const string&): " << value << std::endl;
}


void orderDemo(int a, int b) {
    std::cout << "call orderDemo(int, int): " << a << ", " << b << std::endl;
}

void orderDemo(double a, double b) {
    std::cout << "call orderDemo(double, double): " << a << ", " << b << std::endl;
}
//此处会产生ambiguous的问题
void orderDemo(int a, double b) {
    std::cout << "call orderDemo(int, double): " << a << ", " << b << std::endl;
}


void baseTest() {
    print();
    print(66);
    print(7.10);
    print(3, 7);
    print("test");
    print(std::string(" demo"));
}

void constRefTest() {  
    CVDemo  cvd1("ovTest");
    const CVDemo cvd2("constOVTest");
    
    // const 重载 
    cvd1.getStr() = "111";
    cvd2.getStr();  
    
    // 引用限定符重载
    cvd1.control();           // lvalue
    CVDemo("ok").control();    // rvalue
    cvd2.control();           // const lvalue
}

void templateTest() { 
    int x = 2;
    display(x);            
    display(&x);          // display(T*)
    display(std::string("template")); // 特化 
    
    PrintData("msg test ");      
    PrintData(1, 2.5, "three");     // PrintData(Args...)
}

void opTest() {   
    Complex c1(10, 3), c2(63, 7);
    Complex c3 = c1 + c2;
    Complex c4 = -c1;
    Complex c5 = 8.0 + c1;
    
    std::cout << "c1 = " << c1 << std::endl;
    std::cout << "c2 = " << c2 << std::endl;
    std::cout << "c1 + c2 = " << c3 << std::endl;
    std::cout << "-c1 = " << c4 << std::endl;
    std::cout << "8.0 + c1 = " << c5 << std::endl;
    
    ++c1;
    std::cout << "++c1 = " << c1 << std::endl;
    Complex c6 = c2++;
    std::cout << "c2++ = " << c6 << ", c2 = " << c2 << std::endl;
}

void inheritTest() { 
    Derived d;
    Base* bp = &d;
    
    d.run(10);     
    d.run(3.14);    
    
    bp->run(10);    //多态
    bp->run(6.60);  
    
}

void conceptsTest() {
    conceptsDowith(33);      
    conceptsDowith(2.23);    
    conceptsDowith(90L);    
    
    std::string str = "keep";
    forwardDemo(str);            
    forwardDemo(std::move(str)); //   (T&&)
    forwardDemo("abc");      // const string&  
}

void orderTest() {   
    orderDemo(1, 2);      // int, int 
    orderDemo(1, 2.5);    // int, double  
    orderDemo(1.5, 2);    // double, double  
    // orderDemo(1.5, 2.5); // double, double  
}



int main() {
    std::cout << "启动重载测试......" << std::endl;
    
    baseTest();
    constRefTest();
    templateTest();
    opTest();
    inheritTest();
    conceptsTest();
    orderTest();
    
    return 0;
}

代码并不难,认真与上面的分析流程比对即可明白相关的技术点。

四、总结

C++之所以被很多的开发者认为难,有很大一部分在于细节过于繁杂,让很多老程序员都防不胜防。特别是突然进入一个不太熟悉的场景下,出现一些基础的技术性问题是在所难免的。但是只要认真的分析相关技术点,找到基础的不足之处加以补齐,这样的问题其实并不是什么问题。
但实际问题往往是,不少的开发者在经过一段时间的工程实践后,已经不再愿意踏下心来学习基础的知识点,忽视或轻视基础知识点的扩展,这就为开发埋下了一些隐藏的雷。

<think>好的,我现在需要帮助用户了解C++中运算符重载函数的使用方法和实现方式。首先,我得回顾一下用户提供的引用内容,确保回答准确。 用户提供了多个引用,主要涉及运算符重载作为成员函数和全局函数,以及前置和后置自增运算符的实现。引用[2]提到重载运算符是带有特殊名称的函数,operator后面跟运算符符号。引用[3]展示了如何将运算符重载为成员函数和全局函数,比如Complex类的operator+和operator-。引用[4]和[5]则演示了前置和后置++的重载,区分它们的参数和返回类型。 首先,我需要解释运算符重载的基本概念,说明它允许用户自定义类型使用运算符。然后,要区分成员函数和全局函数两种实现方式,举出例子,比如重载+和-。需要提到友元函数的情况,当运算符需要访问私有成员时。同时,必须强调何时应该使用成员函数,何时使用全局函数,比如当左操作数不是当前类的对象时。 接着,要说明运算符重载的规则,比如参数数量、不能创建新运算符等。然后,给出具体的示例,比如重载+、-、前置++和后置++,确保代码示例正确,并解释每个部分的作用。例如,在Complex类中,operator+作为全局函数,operator-作为成员函数。在Test类中,展示+=和++的重载。 还要注意用户可能容易混淆的地方,比如前置和后置++的区别,参数中的占位符int,以及返回值的类型(引用或临时对象)。此外,友元函数的使用场景需要说明,比如当运算符需要对称处理两个操作数时,可能需要声明为友元。 最后,生成相关问题,覆盖运算符重载的注意事项、友元函数的作用、输入输出运算符的实现,以及类型转换运算符的使用。这些问题能帮助用户进一步学习相关知识点。 需要确保回答结构清晰,分步骤解释,代码示例正确,并正确引用提供的资料。同时,语言要口语化,避免使用专业术语过多,保持自然。</think>### C++运算符重载函数详解 #### 一、基本概念与实现方式 运算符重载是通过定义特殊成员函数或全局函数,使自定义类型支持C++内置运算符的操作。其核心是使用$operator$关键字定义运算符函数,例如:$operator+$[^2] **两种实现方式**: 1. **成员函数形式**(推荐用于类相关操作) ```cpp class Complex { public: Complex operator+(const Complex& rhs) { return Complex(real+rhs.real, imag+rhs.imag); } }; ``` 2. **全局函数形式**(适合需要类型转换的场景) ```cpp Complex operator-(const Complex& a, const Complex& b) { return Complex(a.real-b.real, a.imag-b.imag); } ``` #### 二、核心实现规则 1. 运算符函数参数数量由运算符特性决定: - 单目运算符:0个参数(成员函数)或1个参数(全局函数) - 双目运算符:1个参数(成员函数)或2个参数(全局函数) 2. 不可重载的运算符:$::$、$.*$、$? :$等[^1] 3. 至少一个操作数是用户定义类型 #### 三、典型示例解析 **示例1:复数类运算符重载** ```cpp class Complex { public: double real, imag; // 成员函数形式重载+ Complex operator+(const Complex& c) { return Complex(real+c.real, imag+c.imag); } }; // 全局函数形式重载<< ostream& operator<<(ostream& os, const Complex& c) { os << c.real << "+" << c.imag << "i"; return os; } ``` **示例2:自增运算符重载** ```cpp class Counter { int value; public: // 前置++(返回引用) Counter& operator++() { ++value; return *this; } // 后置++(int参数占位) Counter operator++(int) { Counter temp = *this; ++value; return temp; } }; ``` #### 四、特殊场景处理 1. **输入/输出运算符重载**: ```cpp istream& operator>>(istream& is, Complex& c) { is >> c.real >> c.imag; return is; } ``` 2. **友元函数应用**(访问私有成员时): ```cpp class Matrix { friend Matrix operator*(const Matrix& a, const Matrix& b); }; ``` 3. **赋值运算符注意事项**: - 需处理自赋值 - 返回*this的引用 ```cpp MyArray& operator=(const MyArray& other) { if(this != &other) { // 执行深拷贝 } return *this; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值