跟我学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 小时前
基于QT(C++)实现学本科教务系统(URP系统)
数据库·c++·qt
deng-c-f2 小时前
Linux C/C++ 学习日记(49):线程池
c++·学习·线程池
ulias2122 小时前
C++ 的容器适配器——从stack/queue看
开发语言·c++
daidaidaiyu3 小时前
FFmpeg 关键的结构体
c++·ffmpeg
欧特克_Glodon3 小时前
C++医学图像处理经典ITK库用法详解<一>:图像输入输出模块功能
c++·图像处理·itk
一个不知名程序员www4 小时前
算法学习入门---priority_queue(C++)
c++·算法
Pafey5 小时前
C++的左值引用、右值引用以及转发和完美转发
c++
CoderCodingNo5 小时前
【GESP】C++三级真题 luogu-B4414 [GESP202509 三级] 日历制作
开发语言·c++·算法
晨曦夜月5 小时前
笔试强训day7
开发语言·c++·算法
木心爱编程6 小时前
【Qt 5.14.2 新手实战】QTC++入门筑基——按钮与标签联动:QPushButton + QLabel 实现图片切换器
java·c++·qt