模板推导
PT expr PT结果 T
T& int int& int
T& int& int& int
T& const int const int& const int
T& const int& const int& const int
T& int* int *& int*
T& const int* int const*& int const*
const T& const int const int& int
const T& const int& const int& int
const T& int const int& int
const T& int& const int& int
T* int* int* int
T* const int* const int* const int
const T* int* const int* int
const T* const int* const int* int
T int int int
T int& int int
T const int int int
T const int& int int
T int* int* int*
T const int* const int* const int*
上面蓝色处是因为模板推导为值传递时,expr为const int,const int&时根本无意义,值传递是直接复制的,无法保证其const 属性。
template<typename T>
void f(ParamType param);
在整个编译期间,编译器用expr来推导两个类型:一个是T ,另一个是ParamType 。这两个类型常常是不一样的,
因为ParamType常常包含一些修饰符。(比如const或引用的限定) 对T类型的推导,不仅仅取决于expr ,同时也取决于ParamType
这里有三种情况
1.ParamType是指针或引用,但不是一个universal引用
2.ParamType是universal引用
3.ParamType不是指针也不是引用
1.ParamType是指针或引用,但不是一个universal引用.(即ParamType是T&,const T&,T*,const T*)
替换流程是1.(expr是引用,去除引用,剩下的类型匹配ParamType决定T)
2.待续
3.ParamType不是指针也不是引用.(即ParamType是T) 这意味着,param将成为传入参数的一份拷贝,一个全新的对象,可以被修改的对象.
替换流程是1.expr先去除引用,再去除用const和volatile(如果包含),剩下的类型匹配ParamType决定T)
字符串参数:
template<typename T>
void f(T param);
cosnt char* cosnt ptr = "Fun with pointers";
f(ptr); //T是const char*,param的类型是const char*
数组参数:
template<typename T>
void f(T param);
const char name[] = "J.P.Briggs";
f(name); //name是数组,但是T被推导成cosnt char*
特殊:
template<typename T>
void f(T& param); //T被推导成cosnt char[13],param -> cosnt char(&)[13]或者
函数参数:
void someFunc(int, double);
template<typename T>
void f1(T param);
template<typename T>
void f2(T& param);
f1(someFunc); //T是void (*)(int, double)
f2(someFunc); //T是 void (&)(int, double)
你要记住的事
在template类型推导的时候,references类型的参数被当成non-references。也就是说引用属性会被忽略。
当推导universal类型的引用参数时,左值参数被特殊对待。
当推导传值(by-value)类型参数时,cosnt 和/或 volatile 参数被当成 non-const 和 non-volatile。
当推导类型是数组或函数时会退化成指针,除非形参是引用.
右值一般情况:
函数返回值就是右值.
auto * a = &x;//a -> int*,auto被推导为int
auto b = &x;//b -> int*,auto被推导为int*
auto & c = x;//c -> int&,auto被推导为int
auto d = c;//d -> int,auto被推导为int
const auto e = x;//e -> const int
auto f = e;//f -> int
const auto& g = x;//g -> const int&
auto& h = g;//h -> const int&
a和c的推导结果是很显然的,auto在编译时被替换为int,因此a和c分别被推导为int*和int&。
b的推导结果说明,其实auto不声明为指针,也可以推导出指针类型。
d的推导结果说明当表达式是一个引用类型时,auto会把引用类型抛弃,直接推导成原始类型int。
e的推导结果说明,const auto会在编译时被替换为const int。
f的推导结果说明,当表达式带有const(实际上volatile也会得到同样的结果)属性时,auto会把const属性抛弃掉,推导成non-const类型int。
g、h的推导说明,当auto和引用(换成指针在这里也将得到同样的结果)结合时,auto的推导将保留表达式的const属性。
通过上面的一系列示例,可以得到下面这两条规则:
1)当不声明为指针或引用时,auto的推导结果和初始化表达式抛弃引用和cv限定符后类型一致。
2)当声明为指针或引用时,auto的推导结果将保持初始化表达式的cv属性。
比如上面例子中的auto,和下面的模板参数自动推导出来的类型是一致的:
template <typename T> void func(T x) {} // T -> auto
template <typename T> void func(T * x) {} // T * -> auto *
template <typename T> void func(T & x) {} // T & -> auto &
template <typename T> void func(const T x) {} // const T ->
const auto
template <typename T> void func(const T * x) {} // const T * ->
const auto *
template <typename T> void func(const T & x) {} // const T & ->
const auto &
因此,在熟悉auto推导规则时,可以借助函数模板的参数自动推导规则来帮助和加强理解。
#include <iostream>
#include <type_traits>
#include <string>
using namespace std;
template<typename T>
void fun(T p)
{
std::cout<<std::is_same<T, const int*>::value<<endl;
}
int main()
{
int j = 0;
const int i = 0;
const int ci=i,&cr=i;
auto a=ci; //a为int(忽略顶层const)
auto b=cr; //b为int(忽略顶层const,cr是引用)
auto c=&i; //c为const int *
auto d=&ci; //d是const int *(&ci为底层const)
std::cout<<std::is_same<decltype(cr), const int &>::value<<endl;
std::cout<<std::is_same<decltype(a), int>::value<<endl;
std::cout<<std::is_same<decltype(b), int>::value<<endl;
std::cout<<std::is_same<decltype(c), const int*>::value<<endl;
std::cout<<std::is_same<decltype(d), const int*>::value<<endl;
std::cout<<std::is_same<decltype(&i), const int*>::value<<endl;
const int *pp = &i;
fun(pp);
}
全为1
auto x = 11; //类型3, auto推导为int
const auto cx = x; // 类型3, auto推导为int, cx类型为int const
auto& rx = x; //类型1, auto推导为int, rx类型为int&
auto&& uref1 = x; //左值,auto推导为int&, uref1类型为int&
auto&& uref2 = cx; //左值,auto推导为const int&, uref2类型为const int&
auto&& uref3 = 11; //右值,auto推导为int, uref3类型 int&&
const char name[] = "C++11" ;
auto arr1 = name; // auto推导为const char*, arr1类型const char*
auto& arr2 = name; //auto推导为const char[6], arr2类型为const char(&)[6] void someFunc(int, double);
auto func1 = someFunc(); //auto推导为void(*)(int, double), func1类型为void(*)(int, double)
auto& func2 = someFunc();//auto推导为void(int, double),func2类型为void(&)(int, double)