当前位置: 代码迷 >> 综合 >> Effective Modern C++ 笔记一: 型别推导
  详细解决方案

Effective Modern C++ 笔记一: 型别推导

热度:28   发布时间:2024-01-30 11:20:47.0

 

读书笔记,外加一些自己测试的栗子。这本书讲了一些11 和 14的特性。


条款一:理解模板型别推导

形如:

template<typename T>
void f(ParamType param);f(expr); //从expr来推导T和ParamType的型别

情况一:ParamType是个指针或引用,但不是个万能引用

 

1.若expr具有引用型别,先将引用部分忽略。

2.再进行模式匹配。

 

情况二: ParamType是个万能引用

 

1.如果expr是个左值,T和ParamType都会被推导为左值引用。

注:这是在模板型别推导中,T被推导为引用型别的唯一情形。 尽管在声明时是右值引用语法,型别推导的结果是左值引用。

2.如果expr是个右值,则应用1中规则。

 

template<typename T>
void f(T&& param);int x = 27;
const int cx = x;
const int& rx = x;f(x);//x为左值,所以T为int&, param也是int&f(cx);//cx是个左值,所以T的型别是const int&,param也是f(rx);//rx是左值,所以 T为const int&,param也是f(27);//为右值,所以T为int, param为int&&

 

情况三: ParamType既非指针也非引用

 

忽略引用 const性 volatile也忽略。(按值形参处被忽略。 若实参是const的引用或指针,expr的常量性会在推导过程中保留。)

template<typename T>
void f(T param);int x = 27;
const int cx = x;
const int& rx = x;f(x);f(cx);f(rx);
//以上T和param的类型都为int

考虑

template<typename T>
void f(T param);const char* const ptr = "Fun with pointers";f(ptr); //T 为 const char*  (指针在推导过程中会按值传递给(按比特复制给param),所以指针本身的常量性会被忽略,但是指向的对象的常量性会得到保留。)

 

  • 数组实参 
const char name[] = "JP";const char* ptrToName = name;template<typename T>
void f(T param);
f(name);//name 是个数组,但T被推导为 const char*//特别的
template<typename T>
void f(T& param);f(name);//这种情况下T会被推导成实际的数组类型。  T为const char[13]  f的形参类型为const char(&)[13]

可以利用这一特性在编译器确定数组长度。

template<typename T, std::size_t N>
constexpr std::size_t arraysize(T(&)[N]) noexcept
{return N;
}//constexpr 使其返回值在编译期就可以用。int keyVals[] = {1,3,7,9,11,22,35};
//example
std::array<int, arraysize(keyVals)> mappedVals;

 

  • 函数实参

和数组类型相似,函数型别也同样会退化成函数指针。

void someFunc(int, double);template<typename T>
void f1(T param);template<typename T>
void f2(T& param);f1(someFunc); // param被推导为函数指针, void(*)(int, double);f2(someFunc); // param被推导为函数引用, void(&)(int, double);

条款二:理解auto型别推导

类似于模板推导。

所以:

情况一:类型饰词是指针或引用,但不是万能引用。

情况二:类型饰词是万能引用。

情况三:既非指针也非引用。

 

 

auto x = 27;//3
const auto cx = x;//3
const auto& rx = x;//1auto&& uref1 = x;//x的类别是int, 且是左值,所以uref1的类型是int&
auto&& uref2 = cx;//cx的类型是const int ,且是左值,所以uref2的类型是const int&
auto&& uref3 = 27;//27的类型是int,且是右值,所以uref3的类型是int&&

同样的:

const char name[] = "R. N. Briggs";//name的型别是 const char[13]auto arr1 = name; //arr1的型别是const char*auto& arr2 = name; //arr2的型别是const char (&)[13]void someFuc(int, double);//someFunc是个函数 , 型别是void(int, double)auto func1 = someFunc;//func1的型别是void(*)(int, double)auto& func2 = someFunc;//func2的型别是void(&)(int, double)

区别:

auto x1 = 27;//型别是int, 值是27auto x2(27);//同上auto x3 = {27};//型别是std::initializer_list<int>, 值是{27}auto x4{27};//同上//特别的auto x5 = {1, 2, 3.0}; //错误! 推导不出std::initializer_list<T> 中的T

对于大括号初始化表达式的处理方式,是auto型别推导和模板类型推导唯一不同之处。

 

auto会假定用大括号括起的初始化表达式代表一个std::initializer_list,模板则不会

auto x = {11,23,9}; // x为std::initializer_list<int>template<typename T>
void f(T param);f({11, 23, 9}); //无法通过编译template<typename T>
voidf(std::initializer_list<T> initList);f({11,23,9}); //T的型别推导为int, 从而initlist的型别为std::initializer_list<int>

注意:

c++14中:

auto作为返回值推导和lambda形参类别时,也不能使用大括号的初始化表达式。

 


条款三:理解decltype

template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i)
-> decltype(c[i])
{//do somethingreturn c[i];
}

auto说明了使用了c++11中的返回值型别尾序语法,即该函数的返回值类型将在形参列表之后(在->之后)。

 

考虑:

template<typename Container, typename Index>//c++ 14 不甚正确
auto authAndAccess(Container& c, Index i)
{//do somethingreturn c[i];//返回值型别是根据c[i]推导出来
}std::deque<int> d;
authAndAccess(d, 5) = 10;//无法通过编译

auto推导出来为int ,左值无法赋值。

template<typename Container, typename Index>//c++ 14 能够运作 仍需改进
decltype(auto) authAndAccess(Container& c, Index i)
{//do somethingreturn c[i];//返回值型别是根据c[i]推导出来
}

这样指定用decltype的规则,返回T&.

同样的:

Widget w;const Widget& cw = w;auto myWidget1 = cw; // 非引用 非指针 myWidget1 推导出的类型为Widgetdecltype(auto) myWidget2 = cw;//myWidget1 推导出的类型为const Widget&

更进一步的:

上面的例子中不支持接受右值传递,

修改为:

template<typename Container, typename Index>//c++ 14 最终版
decltype(auto) authAndAccess(Container&& c, Index i)
{//do somethingreturn c[i];//返回值型别是根据c[i]推导出来
}

注意:

int x = 0;
decltype(x);//int
decltype((x));//int&

 

decltype(auto) f1()
{int x = 0;return x;//decltype(x) 是int, 所以f1返回的是int
}decltype(auto) f2()
{int x = 0;return (x);//decltype((X))是int&,所以f2返回的是int&
}

 


条款四:掌握查看型别推导结果的方法

 

1.IDE

2.编译器诊断

3.运行时输出