前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Effective Modern C++(11&14)]Chapter 1: Deducing Types

[Effective Modern C++(11&14)]Chapter 1: Deducing Types

原创
作者头像
昊楠Hacking
发布2018-05-26 12:21:46
5510
发布2018-05-26 12:21:46
举报

1. Understand template type deduction.

  • 函数模板的原型
代码语言:txt
复制
template<typename T>
void f(ParamType param);
    • ParamType是一个左值引用或者指针时
代码语言:txt
复制
template<typename T>
void f(T& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x);// T是int,param类型是int&
f(cx);// T是const int, param类型是const int& 
f(rx); // T是const int, param类型是const int&

template<typename T>
void f(const T& param);

f(x); // T是int, param的类型是const int&
f(cx); // T是int, param的类型是const int&
f(rx); // T是int, param的类型是const int&

const int* px = &x;
f(&x); //T是int, param的类型是int*
f(px); //T是const int, param的类型是const int*
      • 规则:
        • 如果函数调用的表达式中,实参是一个引用,那么就忽略掉引用部分
        • 然后把表达式和ParamType进行匹配来确定类型T.
    • ParamType是一个通用引用时
代码语言:txt
复制
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类型是const int&
f(rx); //rx是左值,T是const int&,param类型是const int&
f(27);//27是右值,T是int,param类型是int&&
      • 此处的规则是按照通用引用参数的实例化规则来实现的
    • ParamType既不是指针也不是引用时
代码语言:txt
复制
template<typename T>
void f(T param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x);  // T和param的类型都是int
f(cx);// T和param的类型都是int
f(rx); // T和param的类型都是int
      • 规则:按值传递的时候,忽略掉参数的引用性,const属性,和volatile属性
      • 例外:
代码语言:txt
复制
template<typename T>
void f(T param);

const char* const ptr = "fun with pointers";

f(ptr);
//此时只会忽略掉指针本身的const
//但是对于指向对象的const会被保留
//即ptr的类型是const char*
    • 传入的参数是数组时
代码语言:txt
复制
const char name[] = "J.P.Briggs"; 
//name的类型是const char[13]
const char* ptrToName = name;  
//数组蜕化成指针

template<typename T>
void f(T param);

f(name); 
//按值传递给模板函数的数组
//模板参数会被推导成指针
//name的类型是const char*

template<typename T>
void f(T& param);

f(name); 
//按引用传递给模板函数的数组
//类型仍然是数组,name的类型是const char[13]
//而模板参数类型是 const char(&)[13]
    • 传入的参数是函数时
代码语言:txt
复制
void someFunc(int, double);

template<typename T>
void f1(T param);

template<typename T>
void f2(T& param);

// 参数被推导为函数指针,param类型是 void (*)(int, double)
f1(someFunc); 
// 参数被推导为函数引用,param类型是void (&)(int, double)
f2(someFunc); 

2. Understand auto type deduction

  • auto的推导方式几乎和模板函数推导方式一样,仅仅除了初始化列表的推导方式有所区别
    • 模板函数拒绝推导初始化列表的右值
    • auto可以将初始化列表推导为std::initializer_list<T>
    • 例如:
代码语言:txt
复制
auto x1 = 27; //类型是int, 值是27
auto x2 (27); //类型是int, 值是27
auto x3 = {27}; //类型是std::initializer_list<int>,值是{27}
auto x4 {27}; //类型是std::initializer_list<int>,值是{27}
auto x5 = {1,2,3.0}; // 错误,值类型不止有一种
auto x6 = {11,9,3}; //类型被推导为std::initializer_list<int>
template<typename T>
void f(T param);
f({11,23,9}); //错误,模板函数拒绝推导
template<typename T>
void f(std::initializer_list<T> initlist);
f({11,23,9}); //正确,参数被推导为std::initializer_list<int>
    • C++14允许函数的返回值使用auto来自动声明返回值类型,也允许对lambda表达式的参数使用auto自动声明,但是对于初始化列表类型仍然不能自动推导:
代码语言:txt
复制
auto createInitList()
{
    return {1,2,3}; //错误,不能推导返回值类型为初始化列表的值   
}

std::vector<int> v;
...
auto resetV = [&v](const auto& newValue) { v = newValue; };
...
resetV({1,2,3}); //错误,不能推导模板函数为初始化列表的值

3. Understand decltype

  • C++decltype使用的第一个场景是声明一个函数模板,它的返回值类型依赖于参数类型,常见与std::vector, std::deque
    • 例子1:
代码语言:txt
复制
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i) -> decltype(c[i]) 
// 前面使用的auto和类型推导无关,它只是暗示语法使用了C++11的尾置返回类型
{
    authenticateUser();
    return c[i];
}

//C++14的格式
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i)
{
    authenticateUser();
    return c[i]; //本来应该返回对c[i]的引用
}
    • 上述例子中 return ci的类型在推导时会忽略掉引用,结果返回的是一个右值,改进一下
代码语言:txt
复制
//C++14的做法
template<typename Container, typename Index)
decltype(auto) authAndAccess(Container& c, Index i)
{
     authenticateUser();
     return c[i]; //可以返回引用,因为decltype会保留原始类型
}
    • 但是如果想要既能返回右值,又能返回引用,仍然需要再修改一下
代码语言:txt
复制
//C++14做法
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i)
{
      authenticateUser();
      return std::forward<Container>(c)[i]; 
      //因为不知道需要返回的是什么值,因此需要使用完美转发
      //当传入的参数是左值时,就返回引用,传入参数是右值时,就返回右值
}

//C++11
template<typename Container, typename Index>
auto authAndAccess(Container&& c, Index i) -> decltype
(std::forward<Container>(c)[i])
{
     authenticateUser();
     return std::forward<Container>(c)[i];
}
  • decltype也可以用于对变量类型的声明
    • 例如:
代码语言:txt
复制
Widget w;
const Widget& cw = w;
auto myWidget1 = cw; //类型为Widget
decltype(auto) myWidget2 = cw; //类型为const Widget&
  • decltype失效的地方:
    • 对于返回值被()包围起来的值,会产生错误的自动推导类型,因为如果返回值本身是一个左值时,而C++定义表达式(x)也是一个左值,因此decltype(x)就是int&.
代码语言:txt
复制
decltype(auto) f1()
{
    int x = 0;
    ...
    return x; //返回类型为int
}

decltype(auto) f1()
{
    int x = 0;
    ...
    return (x); //返回类型为int&
}  

4. Know how to view deduced types

  • 依靠编译器诊断
    • 利用模板类声明的不完全性,触发编译器警告
代码语言:txt
复制
template<typename T>
class TD;

TD<decltype(x)> xType; //编译器会报告x的类型
TD<decltype(y)> yType;
    • 运行时输出,利用typeid()std::type_info::name来提供类型
代码语言:txt
复制
std::cout<< typeid(x).name();
std::cout<< typeid(x).name();

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Understand template type deduction.
  • 2. Understand auto type deduction
  • 3. Understand decltype
  • 4. Know how to view deduced types
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档