函数重载及SFINAE

1. C语言不支持函数重载

main.c

c 复制代码
// gcc main.c
void f(int x, int y){}

void f(double x, double y){}

int main(){}

使用指令gcc main.c编译是会报错的,因为C语言是不支持函数重载的,报错信息如下:

bash 复制代码
main.c:4:6: error: conflicting types for 'f'; have 'void(double,  double)'
    4 | void f(double x, double y){}
      |      ^
main.c:2:6: note: previous definition of 'f' with type 'void(int,  int)'
    2 | void f(int x, int y){}
      |      ^

换成g++ main.c即可编译成功(或者将main.c改成main.cpp使用gcc main.cpp也可编译成功,即让编译器把代码当作C++处理即可)。

2. 关于函数重载编译器会做的事

a. 将所有对应的"同名"候选函数,做成一个集合。

b.根据函数声明,从集合中移除一些不合适的候选函数。此步骤主要依靠SFINAE。

c.在集合中剩余的函数中,依据参数,挑选一个最合适的函数。如果挑选不到,或者无法决定哪个最合适,会报错。

d.如果步骤c中挑选到了最合适的函数,还会继续做一些检查,比如是否是delete的。

3. SFINAE

3.1 SFINAE简单示例

cpp 复制代码
#include <iostream>
#include <typeinfo>
#include <type_traits>

/* 此函数仅会被占用字节数大于4的类型匹配上 */
template<class T>
typename std::enable_if<(sizeof(T)>4)>::type len() {
    std::cout << "sizeof(" << typeid(T).name() << ") is " << sizeof(T) << std::endl;
}

/* 此函数仅会被占用字节数小于等于4的类型匹配上 */
template<class T>
typename std::enable_if<(sizeof(T) <= 4)>::type len() {
    std::cout << "sizeof(" << typeid(T).name() << ") is " << sizeof(T) << std::endl;
}

auto main()->int {
    len<double>();  // sizeof(double) is 8
    len<float>();   // sizeof(float) is 4
    len<char>();    // sizeof(char) is 1
}

3.2 不使用SFINAE时编译器未匹配到合适的重载函数

cpp 复制代码
#include <iostream>
#include <vector>
#include <array>
#include <typeinfo>

template<class T, size_t N>
size_t foo(const T(&)[N]) {
    std::cout << "array's size: " << N << std::endl;
    return N;
}

template<class T>
typename T::size_type foo(const T& container) {
    std::cout << "container's size: " << container.size() << std::endl;   // 错误 C2039	"size": 不是 "Bar" 的成员
    return container.size();                                              // 错误 C2039	"size": 不是 "Bar" 的成员
}

size_t foo(...) {
    std::cout << "variadic" << std::endl;
    return 0;
}

class Bar {
public:
    using size_type = size_t;
};

auto main()->int {
    int a[] = {1,2,3,4,5};
    std::vector<short> vec{ 5,6,7,8,9,0 };
    foo(a);
    foo(vec);
    foo(4);

    //std::cout << typeid(Bar::size_type).name() << std::endl;
    foo(Bar());  // !!!报错是因为此句!!!
}

对上述代码进行编译的时候会报错,报错信息如注释。

本想让foo(Bar());匹配到size_t foo(...),但实际匹配到是template<class T> typename T::size_type foo(const T& container),而Bar却没有size()方法。

3.3 使用SFINAE时让编译器匹配到合适的重载函数

SFINAE"Substitution Failure Is Not An Error"替换失败不是个错误。

cpp 复制代码
#include <iostream>
#include <vector>
#include <array>
#include <typeinfo>

template<class T, size_t N>
size_t foo(const T(&)[N]) {
    std::cout << "array's size: " << N << std::endl;
    return N;
}

/* 通过T().size()的方式使得所有没有size()方法的类无法匹配该函数,从而达到SFINAE的目的 */
template<class T>
decltype(T().size(), typename T::size_type()) foo(const T& container) {
    std::cout << "container's size: " << container.size() << std::endl;
    return container.size();
}

size_t foo(...) {
    std::cout << "variadic" << std::endl;
    return 0;
}

class Bar {
public:
    using size_type = size_t;
};

auto main()->int {
    int a[] = {1,2,3,4,5};
    std::vector<short> vec{ 5,6,7,8,9,0 };
    foo(a);      // array's size: 5
    foo(vec);    // container's size: 6
    foo(4);      // variadic

    std::cout << typeid(Bar::size_type).name() << std::endl;  // m
    foo(Bar());  // variadic

    decltype(int()) ab;
    std::cout << double() << ", " << int() << std::endl;  // 0, 0
}

3.4 利用std::enable_if实现SFINAE让编译器匹配到合适的重载函数

cpp 复制代码
class Bar {
public:
    using size_type = size_t;
};

template<class T, size_t N>
size_t foo(const T(&)[N]) {
    std::cout << "array's size: " << N << std::endl;
    return N;
}

template<class T, class U = typename std::enable_if<!std::is_same_v<T, Bar>>::type>
typename T::size_type foo(const T& container) {
    std::cout << "container's size: " << container.size() << std::endl;
    return container.size();
}

size_t foo(...) {
    std::cout << "variadic" << std::endl;
    return 0;
}

auto main()->int {
    int a[] = {1,2,3,4,5};
    std::vector<short> vec{ 5,6,7,8,9,0 };
    foo(a);
    foo(vec);
    foo(4);

    //std::cout << typeid(Bar::size_type).name() << std::endl;
    foo(Bar());
}

输出:

bash 复制代码
array's size: 5
container's size: 6
variadic
variadic

4. std::enable_ifstd::is_same用法拓展

4.1 示例一

cpp 复制代码
#include <iostream>
#include <type_traits>

class CFoo1{
public:
    using size_type = unsigned int;
};

class CFoo2{
public:
    using size_type = unsigned int;
};

template<class T, typename std::enable_if<std::is_same<T, CFoo1>::value>::type* = nullptr>
void bar(){
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

template<class T, typename std::enable_if<std::is_same<T, CFoo2>::value>::type* = nullptr>
void bar(){
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

auto main()->int {
    bar<CFoo1>();
    bar<CFoo2>();
}

输出:

bash 复制代码
void bar() [with T = CFoo1; typename std::enable_if<std::is_same<T, CFoo1>::value>::type* <anonymous> = 0]
void bar() [with T = CFoo2; typename std::enable_if<std::is_same<T, CFoo2>::value>::type* <anonymous> = 0]

4.2 示例二

与示例一的不同之处在于非类型的模板参数是否有默认值,示例一有默认值在调用时第二个参数可以不给,示例二无默认值调用时需要显示实例化。

cpp 复制代码
#include <iostream>
#include <type_traits>

class CFoo1{
public:
    using size_type = unsigned int;
};

class CFoo2{
public:
    using size_type = unsigned int;
};

template<class T, typename std::enable_if<std::is_same<T, CFoo1>::value>::type* /*= nullptr*/>
void bar(){
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

template<class T, typename std::enable_if<std::is_same<T, CFoo2>::value>::type* /*= nullptr*/>
void bar(){
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

auto main()->int {
    bar<CFoo1, nullptr>();
    bar<CFoo2, nullptr>();
}

输出:

bash 复制代码
void bar() [with T = CFoo1; typename std::enable_if<std::is_same<T, CFoo1>::value>::type* <anonymous> = 0]
void bar() [with T = CFoo2; typename std::enable_if<std::is_same<T, CFoo2>::value>::type* <anonymous> = 0]

4.3 示例三

cpp 复制代码
#include <iostream>
#include <typeinfo>
#include <type_traits>

template<class T>
typename std::enable_if<(sizeof(T)>4)>::type len() {
   std::cout << "sizeof(" << typeid(T).name() << ") is " << sizeof(T) << std::endl;
}

template<class T>
typename std::enable_if<(sizeof(T) <= 4)>::type len() {
   std::cout << "sizeof(" << typeid(T).name() << ") is " << sizeof(T) << std::endl;
}

auto main()->int {
   len<double>();  // sizeof(double) is 8
   len<float>();   // sizeof(float) is 4
   len<char>();    // sizeof(char) is 1
}
相关推荐
2401_8318249616 小时前
基于C++的区块链实现
开发语言·c++·算法
汉克老师17 小时前
GESP5级C++考试语法知识(六、链表(一)单链表)
c++·链表·单链表·快慢指针·进阶·gesp5级·gesp五级
m0_5180194817 小时前
C++与机器学习框架
开发语言·c++·算法
qq_4176950517 小时前
C++中的代理模式高级应用
开发语言·c++·算法
学嵌入式的小杨同学17 小时前
STM32 进阶封神之路(十九):ADC 深度解析 —— 从模拟信号到数字转换(底层原理 + 寄存器配置)
c++·stm32·单片机·嵌入式硬件·mcu·架构·硬件架构
xiaoye-duck18 小时前
《算法题讲解指南:动态规划算法--路径问题》--5.不同路径,6.不同路径II
c++·算法·动态规划
ambition2024218 小时前
最大子数组和算法全解析:从暴力枚举到动态规划优化
数据结构·c++·算法
qq_4614893318 小时前
C++与Qt图形开发
开发语言·c++·算法
小菜鸡桃蛋狗20 小时前
C++——类和对象(上)
开发语言·c++
2401_8795034120 小时前
C++中的观察者模式变体
开发语言·c++·算法