跟我学C++中级篇——重载问题分析之函数模板重载的问题

一、模板对重载的影响

在C++的泛型编程中,类模板和函数模板其实都会指向模板实例化后的重载问题。这些重载问题,除了前面提到的普通函数中的相关重载的问题外,还有一些模板中特定的应用特点。而大家都明白,C++中的模板技术可是相当复杂的。虽然对于大多数的开发者来说,可能对模板中的重载其实用到得并不多。不过,技术拓展一下,一定是好事。

当然,对于想涉足模板编程的开发者来说,更是应该理解模板相关的重载技术问题。

二、主要的问题

  1. 模板函数与普通函数的重载问题
    模板函数与普通函数的重载是不可避免的存在的问题,只要写重载,它就是真实存在的,只不过很多开发者有意无意的绕过去了
  2. 类模板中的成员函数的重载问题
    类模板中同样存在着普通类那样的成员函数重载的问题,没有什么本质的不同
  3. 特化和重载的问题
    特化和重载的问题,往往不是问题而是对技术理解的深度不够。重点还是要把握模板特化的技术点
  4. SFINAE和concepts在重载中限制问题
    SFINAE和concepts技术在重载中可能会导致某些开发者认为事实上的重载函数可能根本无法实现,这才是根本的问题
  5. 完美转发与重载的问题
    完美转发与重载的主要问题在于,虽然它叫完美,但未必最匹配。还是要回到重载的匹配原则和具体的优先级处理

三、分析和解决

  1. 模板函数与普通函数的重载问题
    这里回头再看一下前文提到的重载崩溃的问题:
c 复制代码
#include <iostream>

void print(int x) { std::cout << x << std::endl; }

template <typename T> void print(T &&x) {
  print(x); // 无限递归
}

int main() {
  print(100);
  return 0;
}

另外一个重要的问题就是,一定要掌握在相同的条件下,普通函数的匹配性要比模板函数的匹配性要高。还有,对于一些优先级的匹配,需要掌握好前面的重载相关的技术细节问题。当然,对于必须调用模板函数的情况,可以用显式的指定模板参数的调用方式来实现。

  1. 类模板中的成员函数的重载问题
    这个问题其实已经分析过了,看一下代码:
c 复制代码
struct Parent {
    void func(int) {   }
    virtual void func(double) { }
};

struct Sub : Parent {
    //子类隐藏了父类的同名函数
    void func(const char*) {   }
};

在上面的代码中,如果想直接调用父类中的函数,可以显式的指定类似"d.Parent::func(a);"

  1. 特化和重载的问题

要想掌握特化和重载的问题,首先就得掌握什么是特化?偏特化、全特化和全实例化。这里不展开这些技术细节,可参看前面的模板系统文章。先看一下代码:

c 复制代码
#include <iostream>

template<typename T>
void Test(T value) {  
    std::cout << "call general template Test " << std::endl;
}

template<>
void Test<int>(int v) {  //  特化
    std::cout << "call specialization template Test"<< std::endl;
}

template<typename T>
void Test(T* p) {  //此处是重载
    std::cout << "call general template ov Test" << std::endl;
}
int main(){
    int a = 10;
    Test(a);    // 特化
    Test(&a);   // 重载
    Test(6.8); // 模板
    return 0;
}

解决这类问题重点不在于重载,而在于特化。只有把特化掌握好,才能真正解决这类问题。

  1. SFINAE和concepts的限制问题
c 复制代码
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
Control(T t) {  //限制整型
    std::cout << "limit integral"  << endl;
}

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value>::type  
Control(T t) {  //限制浮点
    cout << "limit floating " << endl;
}
int main(){
    handle("test"); // SFINAE导致无法形成重载函数
    return 0;
}

概念也存在这类问题,一定要注意。具体的处理方法可以使用一些IDE或在线工具,看一下模板实例后的代码就清晰明白了。当然,最根本的仍然是掌握SFINAE和Concepts相关的技术。如果平时用不到这些技术或者对模板不感兴趣的可以忽略。

  1. 完美转发与重载的问题
c 复制代码
#include <iostream>

template<typename T>
void run(T&& v) {  //万能引用
    std::cout << "call universal reference "<< std::endl;
}

template<typename T>
void run(vector<T>& v) {  // 更匹配
    std::cout << "call matching"<< std::endl;
}

int main(){
    vector<int> vec{1};
    run(vec);  
    run(std::move(vec)); // 调用通用引用版本!
    return 0;
}

不要被完美这两个字给迷惑了。模板函数只能是遵循着重载相关的原则和优先级的处理来获取最优的适配性。所以根本还是要把重载的相关的技术细节掌握清楚。另外,还是要对完美转发的推导认真分析透彻(‌万能引用(T&&)和引用折叠规则)。

四、总结

模板的复杂和难度,让不少的开发者畏惧。但技术本身就是纸老虎,你硬它就软,你软它就硬。所以只要大家感于直面困难,就没有解决不了的问题。正如诗云:"世上无给事,只要肯登攀"。与诸君共勉!

相关推荐
仟濹1 小时前
【C/C++】经典高精度算法 5道题 加减乘除「复习」
c语言·c++·算法
kk”2 小时前
C++ map
开发语言·c++
共享家95272 小时前
特殊类的设计
开发语言·c++
信奥卷王3 小时前
2025年9月GESPC++三级真题解析(含视频)
开发语言·c++·算法
qq_433554544 小时前
C++ 稀疏表
开发语言·c++·算法
Bona Sun4 小时前
单片机手搓掌上游戏机(十二)—esp8266运行gameboy模拟器之编译上传
c语言·c++·单片机·游戏机
帅中的小灰灰4 小时前
C++编程观察者设计模式
数据库·c++·设计模式
MSTcheng.5 小时前
【C++STL】priority_queue 模拟实现与仿函数实战
开发语言·c++
还有几根头发呀5 小时前
从 C++ 的角度,系统地解释 进程(Process)、线程(Thread)、协程(Coroutine) 的概念、原理、优缺点,以及常见应用场景。
c++