美文网首页C/C++
Effective Modern C++ 学习笔记

Effective Modern C++ 学习笔记

作者: 猿哥媛姐 | 来源:发表于2018-03-26 08:16 被阅读45次

    第一章:类型推导

    item1:理解模板类型推导

    考虑下面的函数模板

    template<typename T>
    void fun(ParamType param)
    

    调用

    fun(expr);//使用表达式调用f
    

    编译器编译的时候要利用expr推导两个类型:T和ParamType。

    T的推导取决于expr的类型和ParamType的类型。分三种情况

    • ParamType为指针和非通用引用
    • ParamType为通用引用
    • ParamType不是指针也不是引用

    case1:ParamType为指针和非通用引用


    推导规则
    1.expr是引用,忽略其引用属性
    2.剩下部分与ParamType匹配决定T的类型

    template<typename T>
    void f(T & param);  //param是一个引用
    
    int x = 27; // x is an int
    const int cx = x; // cx is a const int
    const int& rx = x; // rx is a reference to x as a const int
    
    f(x); // T is int, param's type is int&
    f(cx); // T is const int, param's type is const int&
    f(rx); // T is const int, param's type is const int&
    
    

    expr的const属性在下面这种情况中被忽略

    template<typename T>
    void f(const T& param); // param is now a ref-to-const
    

    指针的情况和引用基本一样

    template<typename T>
    void f(T* param); // param is now a pointer
    int x = 27; // as before
    const int *px = &x; px is a ptr to x as a const int
    f(&x); // T is int, param's type is int*
    f(px); // T is const int, param's type is const int*
    

    case2:ParamType为通用引用


    template<typename T>
    void f(T&& param); // param is now a universal reference
    int x = 27; // as before
    const int cx = x; // as before
    const int& rx = x; // as before
    f(x); // x is lvalue, so T is int&,  param's type is also int&
    f(cx); // cx is lvalue, so T is const int&,  param's type is also const int&
    f(rx); // rx is lvalue, so T is const int&, param's type is also const int&
    f(27); // 27 is rvalue, so T is int, param's type is therefore int&&
    

    case3:ParamType不是指针也不是引用


    template<typename T>
    void f(T param); // param is now passed by value
    

    1.如果是引用,忽略引用属性
    2.剩下的,忽略const属性,忽略volatile

    int x = 27; // as before
    const int cx = x; // as before
    const int& rx = x; // as before
    f(x); // T's and param's types are both int
    f(cx); // T's and param's types are again both int
    f(rx); // T's and param's types are still both int
    

    下面这种情况,忽略指针自身的const属性,其指向内容的const属性保留,最后param的类型是const char*。

    const char* const ptr = // ptr is const pointer to const object
    f(ptr); // pass arg of type const char * const
    

    数组参数

    数组在下面的情况会弱化为指向const的指针

    template<typename T>
    void f(T param); // template with by-value parameter
    

    而下面的情况会保留数组属性

    template<typename T>
    void f(T& param); // template with by-reference parameter
    

    应用

    // return size of an array as a compile-time constant. (The
    // array parameter has no name, because we care only about
    // the number of elements it contains.)
    template<typename T, std::size_t N> // see info
    constexpr std::size_t arraySize(T (&)[N]) noexcept // below on
    { // constexpr
        return N;
    }
    
    int keyVals[] = { 1, 3, 7, 9, 11, 22, 35 }; // keyVals has
    // 7 elements
    int mappedVals[arraySize(keyVals)]; // so does mappedVals
    

    函数参数

    void someFunc(int, double); // someFunc is a function;
    // type is void(int, double)
    template<typename T>
    void f1(T param); // in f1, param passed by value
    template<typename T>
    void f2(T& param); // in f2, param passed by ref
    f1(someFunc); // param deduced as ptr-to-func;
    // type is void (*)(int, double)
    f2(someFunc); // param deduced as ref-to-func;
    // type is void (&)(int, double)
    

    Things to Remember
    • During template type deduction, arguments that are references are treated as
    non-references, i.e., their reference-ness is ignored.
    • When deducing types for universal reference parameters, lvalue arguments get
    special treatment.
    • When deducing types for by-value parameters, const and/or volatile arguments
    are treated as non-const and non-volatile.
    • During template type deduction, arguments that are array or function names
    decay to pointers, unless they’re used to initialize references.

    item2:理解auto的类型推导

    auto的类型推导和模板的类型推导基本一样,其转换关系是
    1.auto相当于模板推导里面的T
    2.对auto的修饰相当于ParamType

    auto x = 27;
    const auto cx = x;
    const auto& rx = x;
    
    template<typename T> // conceptual template for
    void func_for_x(T param); // deducing x's type
    
    func_for_x(27); // conceptual call: param's
    // deduced type is x's type
    template<typename T> // conceptual template for
    void func_for_cx(const T param); // deducing cx's type
    func_for_cx(x); // conceptual call: param's
    // deduced type is cx's type
    template<typename T> // conceptual template for
    void func_for_rx(const T& param); // deducing rx's type
    func_for_rx(x); // conceptual call: param's
    // deduced type is rx's type
    

    相对应的case1

    相对应的case2

    auto&& uref1 = x; // x is int and lvalue,
    // so uref1's type is int&
    auto&& uref2 = cx; // cx is const int and lvalue,
    // so uref2's type is const int&
    auto&& uref3 = 27; // 27 is int and rvalue,
    // so uref3's type is int&&
    

    相对应的case3

    相对应的数组和函数

    const char name[] = // name's type is const char[13]
    "R. N. Briggs";
    auto arr1 = name; // arr1's type is const char*
    auto& arr2 = name; // arr2's type is
    // const char (&)[13]
    void someFunc(int, double); // someFunc is a function;
    // type is void(int, double)
    auto func1 = someFunc; // func1's type is
    // void (*)(int, double)
    auto& func2 = someFunc; // func2's type is
    // void (&)(int, double)
    

    有一个例外,看定义一个变量并初始化,有以下选择,结果都是一样的

    //C++98
    int x1 = 27;
    int x2(27);
    
    //C++11
    int x3 = { 27 };
    int x4{ 27 };
    

    用auto时,第三四条语句定义的是包含一个元素的std::initializer_list<int> 容器

    auto x1 = 27; // type is int, value is 27
    auto x2(27); // ditto
    
    auto x3 = { 27 }; // type is std::initializer_list<int>,
    // value is { 27 }
    auto x4{ 27 }; // ditto
    

    大括可以直接推导auto,但不能推导模板参数

    auto x = { 11, 23, 9 }; // x's type is
    // std::initializer_list<int>
    template<typename T> // template with parameter
    void f(T param); // declaration equivalent to
    // x's declaration
    f({ 11, 23, 9 }); // error! can't deduce type for T
    

    指定了std::initializer_list<T>是可以推导T的

    template<typename T>
    void f(std::initializer_list<T> initList);
    f({ 11, 23, 9 }); // T deduced as int, and initList's
    // type is std::initializer_list<int>
    

    C++14用auto推导返回值,使用的是模板类型推导规则,下面的是非法的

    auto createInitList()
    {
    return { 1, 2, 3 }; // error: can't deduce type
    } // for { 1, 2, 3 }
    

    lambda用auto做参数推导也是如此

    std::vector<int> v;
    …
    auto resetV =
    [&v](const auto& newValue) { v = newValue; }; // C++14
    …
    resetV({ 1, 2, 3 }); // error! can't deduce type
    // for { 1, 2, 3 }
    

    item3:理解decltype

    decltype 简单的返回传入值的类型

    const int i = 0; // decltype(i) is const int
    bool f(const Widget& w); // decltype(w) is const Widget&
    // decltype(f) is bool(const Widget&)
    struct Point {
    int x, y; // decltype(Point::x) is int
    }; // decltype(Point::y) is int
    Widget w; // decltype(w) is Widget
    if (f(w)) … // decltype(f(w)) is bool
    template<typename T> // simplified version of std::vector
    class vector {
    public:
    …
    T& operator[](std::size_t index);
    …
    };
    vector<int> v; // decltype(v) is vector<int>
    …
    if (v[0] == 0) … // decltype(v[0]) is int&
    

    decltype应用在模板返回值推导上

    template<typename Container, typename Index> // works, but
    auto authAndAccess(Container& c, Index i) // requires
    -> decltype(c[i]) // refinement
    {
    authenticateUser();
    return c[i];
    }
    

    decltype(auto) :auto表示返回值需要推导,decltype表示推导是使用decltype的规则

    template<typename Container, typename Index> // C++14; works,
    decltype(auto) // but still
    authAndAccess(Container& c, Index i) // requires
    { // refinement
    authenticateUser();
    return c[i];
    }
    

    decltype(auto)和auto对比

    Widget w;
    const Widget& cw = w;
    auto myWidget1 = cw; // auto type deduction: myWidget1's type is Widget
    decltype(auto) myWidget2 = cw; // decltype type deduction:myWidget2's type is  const Widget&
    

    上面的例子都有瑕疵,问题出在Container不能传入右值,右值是一个临时对象,在离开函数后会被销毁,这样会返回一个悬置的引用。但是传入右值不是没有意义的,例如当用户只想简单的返回一个元素的拷贝时,如下面代码所示。

    std::deque<std::string> makeStringDeque(); // factory function
    // make copy of 5th element of deque returned
    // from makeStringDeque
    auto s = authAndAccess(makeStringDeque(), 5);
    

    C++14的最终版本

    template<typename Container, typename Index> // final
    decltype(auto) // C++14
    authAndAccess(Container&& c, Index i) // version
    {
    authenticateUser();
    return std::forward<Container>(c)[i];
    }
    

    C++11最终版本

    template<typename Container, typename Index> // final
    auto // C++11
    authAndAccess(Container&& c, Index i) // version
    -> decltype(std::forward<Container>(c)[i])
    {
    authenticateUser();
    return std::forward<Container>(c)[i];
    }
    

    特殊情况

    相关文章

      网友评论

        本文标题:Effective Modern C++ 学习笔记

        本文链接:https://www.haomeiwen.com/subject/zewaqftx.html