C++ 模板函数返回值"重载"
不想看梳理过程的看这里
1 简析(真实应用在3.1,不想看那些梳理的直接跳过去)
1.1 问题产生原因
在正常C++语言应用中,函数重载需要保证重载函数的返回值不变,改变函数的入参参数列表,而模板函数通常可以更加完美地实现更灵活的入参参数定义,达到泛型编程的目的,正常情况下,由于模板函数是在编译时未进入函数时进行的函数推导,推导在函数内条件判断之前,因此如果在正常函数内进行的条件判断来确定返回值时,会产生编译错误,由模板函数推导后的函数只能return一种返回值类型,从而导致模板函数难以进行类似重载的操作。
1.2 突破口 C++11特性
C++ 11 可以利用SFINAE根据类型特征有条件地从重载决策中删除函数。
2 解决利器
2.1 SFINAE
SFINAE全称是"Substitution Failure Is Not An Error",即“替换失败不是错误”
在函数模板的重载决议中应用此规则:当将模板形参替换为显式指定的类型或推导的类型失败时,从重载集中丢弃这个特化,而非导致编译失败。
此特性被用于模板元编程。
2.2 std::enable_if
2.2.1 头文件
#include <type_traits>
2.2.2 原理
此元函数是活用 SFINAE ,基于类型特性条件性地从重载决议移除函数,并对不同类型特性提供分离的函数重载与特化的便利方法。
std::enable_if 可用作额外的函数参数(不可应用于运算符重载)、返回类型(不可应用于构造函数与析构函数),或类模板或函数模板形参。
2.2.3 范例
引用自CppReference
错误方法
struct T {
enum { int_t,float_t } m_type;
template <typename Integer,
typename = std::enable_if_t<std::is_integral_v<Integer>>
>
T(Integer) : m_type(int_t) {}
template <typename Floating,
typename = std::enable_if_t<std::is_floating_point_v<Floating>e>
>
T(Floating) : m_type(float_t) {} // error: cannot overload
};
正确方法
struct T {
enum { int_t,float_t } m_type;
template <typename Integer,
std::enable_if_t<std::is_integral_v<Integer>, int> = 0
>
T(Integer) : m_type(int_t) {}
template <typename Floating,
std::enable_if_t<std::is_floating_point_v<Floating>, int> = 0
>
T(Floating) : m_type(float_t) {} // OK
};
2.2.4 用法解析
auto typeName = std::enable_if<[bool],[typename]>::type
- 在bool条件为true的情况下,typeName 为 typename类型名
- 在bool条件为false的情况下,typeName不存在
2.3 std::is_same
2.3.1 头文件
#include <type_traits>
2.3.2 范例
不引用了,心累
2.3.3 用法解析
auto is_same_v = is_same<[type1], [type2]>::value
- type1类型与type2类型相同时返回is_same_v为true
- type1类型与type2类型不同时返回is_same_v为false
3 解决方案(不想看梳理过程的直接看这节)
3.1 关键代码部分
- 模板定义
#include <type_traits>
#include <string>
template <class T>
typename std::enable_if<std::is_same<T,std::string>::value,T>::type foo()
{
return "This is std::string";
}
template <class T>
typename std::enable_if<std::is_same<T, int>::value, T>::type foo()
{
return 1;
}
- 应用
std::string retString = foo<std::string>();
int retInteger = foo<int>();
- 结果
retString = "This is std::string";
retInteger = 1;
4 写在最后
平时不怎么爱写Blog,基本就是在写代码,虽然写的很烂;下半年考研,估计要丢下一阵子代码去复习了,突然感觉很惶恐,最近全天不休的写代码,希望能够有所收获。