用函数传参来想下面规则,会容易很多
1. 理解模板型别推导
template<class T>
void func(ParamType param); // 这里ParamType可能是
//const、指针、引用修饰的类型T
func(expr); // 通过表达式调用
T的推导不仅与expr有关,还与ParamType有关
expr(实参)的影响
- 有引用的实参会被视为无引用,如
int& x = 1;func(x);,T推导出来类型就是int,而非int &
ParamType(形参)也会影响T的推导,先决定形参类型,再推导出T,分为:
- 引用或指针
&、*,形参会保留const修饰,因为底层const指针只能被底层const指针接受 - 万能引用
&&,左值会被推导为左值引用(唯一T为引用的情形),如int x = 1;func(x);//这里T推导出来就是int &,而右值则会去掉引用,T为常规的,如func(27);//T推导出来就是int - 值传递,会去掉修饰变量的const属性,如
const char* const==>const char*
- 两者共同作用
- 如果实参是数组
- 值传递会退化为指针类型,如
char a[10]==>char * - 引用传递,则会保留数组大小,如
const char (&)[10]
//在编译期间返回一个数组大小的常量值(//数组形参没有名字, //因为我们只关心数组的大小) template<typename T, std::size_t N> constexpr std::size_t arraySize(T (&)[N]) noexcept { return N; }- 如果实参是函数
- 值传递还是会退化为指针,
int func(char c)==>int (*)(char) - 引用传递,
int func(char c)==>int (&)(char)
template<class ReturnT,class Param1> void getFuncType(ReturnT (*)(Param1)){ ...; }
2. 理解auto型别推导
- 一般情况下,auto类型推导和template类型推导一样,可以认为auto是template的语法糖
- auto ==> T
- 对auto的修饰 ==> ParamType
int x = 1;
const auto& rx = x; // auto推导为int
//===等价于===
template<class T>
void fun(const T& rx);
func(x); //T推导为int
- 两者唯一区别在于,auto会假定大括号括起来的初始化表达式代表一个std::initializer_list,而template却不会,直接报错
auto x = { 11, 23, 9 }; //x的类型是std::initializer_list<int>
template<typename T> //带有与x的声明等价的
void f(T param); //形参声明的模板
f({ 11, 23, 9 }); //错误!不能推导出T
- cpp14中,auto推导返回值类型、lambda中auto修饰形参时使用的template推导,所以不能推导
{}初始化表达式
这里cpp11不允许auto推导返回值类型,且auto只能推导单条语句的lambda表达式,如
auto lambda = [](int x) { return x * 1.5; };
auto createInitList()
{
return { 1, 2, 3 }; //错误!不能推导{ 1, 2, 3 }的类型
}
auto resetV =
[](const auto& newValue){ ... }; //C++14
resetV({ 1, 2, 3 }); //错误!不能推导{ 1, 2, 3 }的类型
3. 理解decltype
- decltype只是简单的返回名字或者表达式的类型,如
bool f(const Widget& w);
//decltype(w)是const Widget&
//decltype(f)是bool(const Widget&)
- 在 C++11 中,
auto+ 尾置返回类型(-> decltype(...))的主要用途是 解决函数返回值依赖参数类型的场景,尤其是模板函数中返回值与参数类型相关的情况。
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i) //这里auto不是用来推导,只是暗示使用了C++11的**尾置返回类型**语法
->decltype(c[i]) //尾置返回类型,与参数相关
{
return c[i];
}
- 在cpp14中,auto可以直接用来推导返回值类型,但是注意auto会忽视掉实参的引用,所以上面代码去掉decltype,返回的就是
c[i]的临时对象,而如果是使用auto&,则一直都是返回引用; - 需要真正做到按照值返回,可以使用
decltype(auto)(cpp11不支持),这里是提示auto使用decltype的推导方式,也就完美做到按照返回值类型来返回
template<typename Containter, typename Index> //仍然需要改进
decltype(auto) authAndAccess(Container& c, Index i);
- 但是现在并不支持右值传递,所以引入完美引用
template<typename Container, typename Index> //最终的C++11版本
auto
authAndAccess(Container&& c, Index i)
->decltype(std::forward<Container>(c)[i])
{
authenticateUser();
return std::forward<Container>(c)[i];
}
- 将
decltype应用于变量名会产生该变量名的声明类型。比单纯的变量名更复杂的左值表达式,decltype可以确保报告的类型始终是左值引用,如decltype(x)是int,那decltype((x))是int&
decltype(auto) f2()
{
int x = 0;
return (x); //decltype((x))是int&,所以f2返回int&
}
4. 掌握查看型别推导结果的方法
- 类型推断可以从IDE看出,从编译器报错看出,从Boost TypeIndex库的使用看出
- 这些工具可能既不准确也无帮助,所以理解C++类型推导规则才是最重要的