浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第2课 auto类型推导(1)

Posted on 2019-07-14 20:47  浅墨浓香  阅读(895)  评论(0编辑  收藏  举报

第2课 auto类型推导(1)

一、auto类型推导

(一)与模板类型推导映射关系

      1、auto类型推导与模板类型推导可以建立一一映射关系,它们之间存在双向的算法变换。auto扮演模板中T的角色,而变量的类型及修饰词(如const或引用)扮演ParamType的角色

      2、举例说明两者映射关系:(auto相当于T)

      (1)与auto x = 27等价的函数模板

template<typename T>

void func_x(T param); //T ←→ auto,即auto x 相当于这里的T param

func_x(27);

  (2)与const auto cx = x等价的函数模板

template<typename T>

void func_x(const T param); // const auto ←→ const T

func_cx(x);

  (3)与const auto& rx = x;等价的函数模板

template<typename T>

void func_crx(const T& param); // const auto&←→const T&

func_crx(x);

(二)auto类型推导规则(与函数模板推导规则基本一致

  1、推导规则:

    ①规则1:auto& x或auto* x,即指针或引用时。(与函数模板推导规则1类似)

    ②规则2:auto&& x,即万能引用时。(与函数模板推导规则2类似)

    ③规则3:auto x,即按值推导(非指针也非引用,与函数模板推导规则3类似)

  2、注意事项:

    ①auto声明的变量必须马上初始化,以让编译器推断出它的实际类型。

    ②与函数模板推导类似,auto x总是推导出值类型auto&&总是推导出引用类型(可能左值引用,也可能是右值引用)。

    ③auto不能用于函数形参(但可用于lambda表达式形参的自动推导)

【编程实验】auto类型推导

#include <iostream>
#include <boost/type_index.hpp>

using namespace std;
using boost::typeindex::type_id_with_cvr;

//辅助类模板,用于打印T的类型
template <typename T>
void printType(string s)
{
    cout <<s <<" = "<< type_id_with_cvr<T>().pretty_name() << endl;
}

//测试函数
void myfunc(int, double)
{
}

int main()
{
    //规则3:按值推导,如auto x
    auto x = 27;
    const auto cx = x;
    int& a = x;
    auto ra = a; //由于按值推导,a的引用会被忽略,故ra = int

    printType<decltype(cx)>("cx"); //cx = const int
    printType<decltype(ra)>("ra"); //ra = int

    //规则1:auto引用或指针(auto&和auto*)
    const auto& rx = x;          //auto=int, rx=const int&
    const auto* const px = &x;   //auto=int, px=const int* const;
    const volatile auto cvx = x; //cvx = const volatile int;
    auto* cpx = px;     //cpx =  const int*  (注意:只保留px的底层const,而顶层const被舍弃!)
    
    printType<decltype(rx)>("rx"); //rx = const int&
    printType<decltype(px)>("px"); //px=const int* const;
    printType<decltype(cvx)>("cvx"); //cvx=const volatile int;
    printType<decltype(cpx)>("cpx"); //cvx=const int*;

    //规则2:auto&&万能引用
    auto&& uref1 = x; //用左值初始化,返回左值引用。auto=int&,x=int&
    auto&& uref2 = cx;//用左值初始化,返回左值引用。由于是引用,会传为cv属性,即auto=const int&, uref2=const int&
    auto&& uref3 = 27;//用右值初始化,返回右值引用。即auto=int, uref3=int&&
    auto&& uref4 = px;//用左值初始化,返回左值引用,由于引用表示变量本身,所以uref4=const int* const&。(顶层与底层const均保留)

    printType<decltype(uref1)>("uref1"); //uref1 = int&
    printType<decltype(uref2)>("uref2"); //uref2 = const int&
    printType<decltype(uref3)>("uref3"); //uref3 = int&&
    printType<decltype(uref4)>("uref4"); //uref4 = const int* const &

    //数组或函数名类型的推导
    const char name[] = "SantaClaus";
    auto arr1 = name; //退化为数组指针: arr1 = const char*
    auto& arr2 = name;//数组引用: arr2 = const char(&)[11]

    printType<decltype(arr1)>("arr1"); //arr1 = const char*
    printType<decltype(arr2)>("arr2"); //arr2 = const char(&)[11]

    auto func1 = myfunc; //退化为函数指针:func1 = void(*)(int,double)
    auto& func2 = myfunc;//数组引用:func2 = void(&)(int,double)

    printType<decltype(func1)>("func1"); //arr1 = const char*
    printType<decltype(func2)>("func2"); //arr2 = const char(&)[11]

    // //new中使用auto
    auto ax = new auto(1);
    printType<decltype(ax)>("ax"); //ax = int

    return 0;
}
/*输出结果:
cx = int const
ra = int
rx = int const &
px = int const * const
cvx = int const volatile
cpx = int const *
uref1 = int &
uref2 = int const &
uref3 = int &&
uref4 = int const * const &
arr1 = char const *
arr2 = char const (&)[11]
func1 = void (__cdecl*)(int,double)
func2 = void (__cdecl&)(int,double)
ax = int *
*/

二、auto推导的特殊规则

(一)auto声明的变量使用大括号初始化表达式进行初始化时,推导出的类型是initializer_list<T>类型。注意:采用={}初始化的才是initializer_list<T>类型,如果声明时直接在变量后面加{}的(如auto x{14},x为int类型,这里只能有一个元素,多了则编译不过!),则不是initializer_list<T>类型。

(二)C++14中,auto用于推导函数的返回值或lambda形参时,使用的是模板类型的推导而不是auto类型的推导。需要特别注意的是,与auto推导最大的不同在于模板类型推导时不会将{}初始化表达式推导为initializer_list<T>类型,而auto推导会注意,这两者的区别!(见下面《编程实验》)

【编程实验】auto类型推导的特殊规则

#include <iostream>
#include <vector>
#include <boost/type_index.hpp>

using namespace std;
using boost::typeindex::type_id_with_cvr;

//辅助类模板,用于打印T的类型
template <typename T>
void printType(string s)
{
    cout << s << " = " << type_id_with_cvr<T>().pretty_name() << endl;
}

template<typename T>
//void func(initializer_list<T> param)
void func(T param)
{
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;
    cout << "param = " << type_id_with_cvr<decltype(param)>().pretty_name() << endl;
}

//C++14允许auto用于推导函数的返回值
auto func_i()
{
    return 10; //采用模板类型推导,这里与auto的类型推导结果是一样的,返回值为int型。
}

//auto createInitList()
//{
//    //return { 1, 2, 3 }; //编译不通过!auto在返回值类型推导上采用的是模板类型推导。(见下面分析)
//}

template<typename T>
auto createInitList() ->initializer_list<T>
{
    return { 1, 2, 3 };    //由于auto在返回值类型是采用模板类型而不是auto类型的来推导。
                           //而模板类推导时,不会将大括号推导为initializer_list,即return中从语句中的{}(是个
                           //initialize_list<int>类型)到auto采用的是模板类型推导,与func({1,2,3})类似,从这里
                           //推出这个是initializer_list<T>类型,所以必须在尾随的返回值表达式中要手动加
                           //上->initializer_list<T>
}


int main()
{
    auto x1 = 27;    //C++98语法: 类型为int
    auto x2(27);     //同上

    auto x3 = { 27 }; //C++11语法:x3为initializer_list<int> <== 当auto遇到“={}”时(即(大括号赋值初始化)。
    auto x4 {13};     //C++11语法,类型为int,注意这种方式初始化为int,并且只能有一个元素。注意与x3的区别!

    printType<decltype(x1)>("x1");
    printType<decltype(x2)>("x2");
    printType<decltype(x3)>("x3");
    printType<decltype(x4)>("x4");

    //auto类型推导的特殊规则:可以将{}推导为initializer_list<T>类型。
    auto x = { 8, 9, 10 }; //x: initializer_list<int>类型,共有3个元素。
    printType<decltype(x)>("x");

    //模板类型推导时,无法将{}推导为initializer_list<T>类型
    //func({ 8, 9, 10 });  //错误,此处的实参为initializer<int>类型,由于模板类型推导时无法将
                           //{8, 9, 10}推导为initializer_list<T>,编译失败。
                           //如果将形参改为intializer_list<T>,将可以编译成功!

    auto func_ret = createInitList<int>(); //func_ret = class std::initializer_list<int>
    printType<decltype(func_ret)>("func_ret"); 

    auto fi = func_i();  //fi = int
    printType<decltype(fi)>("func_i"); 

    //在lambda表达式形参中用auto声明的变量也不会将{}推导为initializer_list<T>类型。
    std::vector<int> v = {1, 2, 3, 4};
    auto resetV = [&v](const auto& newValue) {  //auto resetV = [&v](const initializer_list<int>& newValue) {
   
        v = newValue;
    };

    //resetV({ 5, 6, 7}); //lambda形参中的auto也是采用模板类型推导,因此推导时{}不会被自动推导为initializer_list<T>

    return 0;
}
/*输出结果:
x1 = int
x2 = int
x3 = class std::initializer_list<int>
x4 = int
x = class std::initializer_list<int>
func_ret = class std::initializer_list<int>
func_i = int
*/