zoukankan      html  css  js  c++  java
  • 引用 一

    https://en.cppreference.com/w/cpp/language/reference

    引用声明

    声明一个变量名字作为引用,也就是,是一个已经存在的对象或函数的别名

    语法

    & attr(optional) declarator
    
    && attr(optional) declarator (since C++11)
    
    1. lvalue(左值)引用声明符:表达式S& D;将D声明为S类型的lvalue(左值)引用
    2. rvalue(右值)引用声明符:表达式S&& D;将D声明为S类型的rvalue(右值)引用

    declarator - 除了引用声明符外的任何声明符(不能有引用的引用)

    attr(C++11) - 可选的属性

    引用必须初始化为一个有效的对象或者函数

    不存在void引用,也不存在引用的引用

    引用类型不能在顶层被cv限定;如果对typedef-name 或者 decltype 或者类型模板形参增加了限定,则忽略

    引用不是对象,没有存储,尽管编译器为了实现预期的语义可能申请空间(比如,非静态数据成员的引用经常增加类的大小)

    因为引用不是对象,所以没有引用数组,没有引用的指针,没有引用的引用

    int& a[3]; // error
    int&* p;   // error
    int& &r;   // error
    

    引用塌陷

    允许通过模板或者typedef中的类型操作来构建引用的引用,这时引用塌陷规则开始起作用:rvalue(右值)引用的rvalue(右值)引用塌陷成rvalue(右值)引用,其他均塌陷成lvalue(左值)引用

    typedef int&  lref;
    typedef int&& rref;
    int n;
    lref&  r1 = n; // type of r1 is int&
    lref&& r2 = n; // type of r2 is int&
    rref&  r3 = n; // type of r3 is int&
    rref&& r4 = 1; // type of r4 is int&&
    

    这条规则与T&&用作函数模板时模板参数推导的特殊规则结合在一起,组成了使std::forward可行

    lvalue(左值)引用

    lvalue(左值)引用可以用作一个存在对象的别名(可选择拥有cv限定)

    #include <iostream>
    #include <string>
     
    int main() {
        std::string s = "Ex";
        std::string& r1 = s;
        const std::string& r2 = s;
     
        r1 += "ample";           // modifies s
    //  r2 += "!";               // error: cannot modify through reference to const
        std::cout << r2 << '
    '; // prints s, which now holds "Example"
    }
    

    也可以用作实现函数调用时的引用传递

    #include <iostream>
    #include <string>
     
    void double_string(std::string& s) {
        s += s; // 's' 与 main() 的 'str' 是同一对象
    }
     
    int main() {
        std::string str = "Test";
        double_string(str);
        std::cout << str << '
    ';
    }
    

    当一个函数的返回值是lvalue(左值)引用时,那么这个函数调用的表达式也变成lvalue(左值)表达式

    #include <iostream>
    #include <string>
     
    char& char_number(std::string& s, std::size_t n) {
        return s.at(n); // string::at() returns a reference to char
    }
     
    int main() {
        std::string str = "Test";
        char_number(str, 1) = 'a'; // the function call is lvalue, can be assigned to
        std::cout << str << '
    ';
    }
    

    rvalue(右值)引用

    rvalue(右值)引用可以用作延长临时对象的生命周期(lvalue(左值)引用也可以延长临时对象的生命周期,但是不能修改)

    #include <iostream>
    #include <string>
     
    int main() {
        std::string s1 = "Test";
    //  std::string&& r1 = s1;           // error: can't bind to lvalue
     
        const std::string& r2 = s1 + s1; // okay: lvalue reference to const extends lifetime
    //  r2 += "Test";                    // error: can't modify through reference to const
     
        std::string&& r3 = s1 + s1;      // okay: rvalue reference extends lifetime
        r3 += "Test";                    // okay: can modify through reference to non-const
        std::cout << r3 << '
    ';
    }
    

    如果函数有rvalue(右值)引用和lvalue(左值)引用的重载,rvalue(右值)引用会绑定到rvalues(右值)(包括prvalues(纯右值)和xvalues(亡值),lvalue(左值)引用绑定到lvalues(左值)

    #include <iostream>
    #include <utility>
     
    void f(int& x) {
        std::cout << "lvalue reference overload f(" << x << ")
    ";
    }
     
    void f(const int& x) {
        std::cout << "lvalue reference to const overload f(" << x << ")
    ";
    }
     
    void f(int&& x) {
        std::cout << "rvalue reference overload f(" << x << ")
    ";
    }
     
    int main() {
        int i = 1;
        const int ci = 2;
        f(i);  // calls f(int&)
        f(ci); // calls f(const int&)
        f(3);  // calls f(int&&)
               // would call f(const int&) if f(int&&) overload wasn't provided
        f(std::move(i)); // calls f(int&&)
     
        //注意这里,右值引用在用作表达式的时候是左值
        int&& x = 1;
        f(x);            // calls f(int& x)
        f(std::move(x)); // calls f(int&& x)
    }
    

    这允许移动构造函数,移动赋值操作符和其他移动函数(比如std::vector::push_back())自动的选择合适的。因为rvalue(右值)可以绑定到xvalues(亡值),可以指向非临时对象

    int i2 = 42;
    int&& rri = std::move(i2); // binds directly to i2
    

    这使得把在作用域不需要的对象移出去成为可能

    std::vector<int> v{1,2,3,4,5};
    std::vector<int> v2(std::move(v)); // binds an rvalue reference to v
    //v变成空,数据移给v2
    assert(v.empty());
    

    转发引用

    转发引用是一个特殊的引用,可以保存函数实参的值类别,使得能利用std::forward转发。转发引用有下面这几种:

    1. 函数模板的函数形参,被声明为没有cv限制的同一个函数模板的模板形参的rvalue(右值)引用
    template<class T>
    int f(T&& x) {                    // x 是转发引用
        return g(std::forward<T>(x)); // 从而能被转发
    }
    int main() {
        int i;
        f(i); // 实参是左值,调用 f<int&>(int&), std::forward<int&>(x) 是左值
        f(0); // 实参是右值,调用 f<int>(int&&), std::forward<int>(x) 是右值,这里调用了f函数,在调用g函数的时候,编译报错,因为不能转化为cv限定的
    }
     
    template<class T>
    int g(const T&& x); // x 不是转发引用:const T 不是无 cv 限定的
     
    template<class T> struct A {
        template<class U>
        A(T&& x, U&& y, int* p); // x 不是转发引用:T 不是构造函数的类型模板形参
                                 // 但 y 是转发引用
    };
    
    1. auto&&是转发引用,除了通过花括号初始化列表推导的情况之外
    auto&& vec = foo();       // foo() 可以是左值或右值,vec 是转发引用
    auto i = std::begin(vec); // 也可以
    (*i)++;                   // 也可以
    g(std::forward<decltype(vec)>(vec)); // 转发,保持值类别
     
    for (auto&& x: f()) {
      // x 是转发引用;这是使用范围 for 循环的最安全方式
    }
     
    auto&& z = {1, 2, 3}; // *不是*转发引用(初始化器列表的特殊情形)
    

    悬挂引用

    尽管,引用一单被初始化,就一直指向合法的对象或者函数,但是有可能创建的程序,在指向的对象生命周期结束时,引用还保存着可以访问的权限(悬挂)。这种引用的行为是未定义的。一个简单的例子就是函数返回自动变量的引用

    std::string& f()
    {
        std::string s = "Example";
        return s; // 退出 s 的作用域:
                  // 调用其析构函数并解分配其存储
    }
     
    std::string& r = f(); // 悬垂引用
    std::cout << r;       // 未定义行为:从悬垂引用读取
    std::string s = f();  // 未定义行为:从悬垂引用复制初始化
    

    上面的行为都是不可取的,会导致未知的问题

    注意rvalue(引用)和const的lvalue(左值)引用都会延长临时对象的生命周期

    如果引用指向的对象被销毁了(比如通过析构函数),但是存储还没有释放,访问在生命周期外的对象在限制情况下可能有效,在同一个空间重新创建这个对象可能合法

    版权声明:本文版权归作者所有,如需转载,请标明出处

  • 相关阅读:
    基于visual Studio2013解决C语言竞赛题之1054抽牌游戏
    基于visual Studio2013解决C语言竞赛题之1053洗牌
    基于visual Studio2013解决C语言竞赛题之1052求根
    CSS样式
    CSS引入方式,高级选择器
    CSS基础,选择器
    html基础
    sql server链接查询
    [置顶]动态网页开发基础【笔记】
    sql server链接查询
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/14576304.html
Copyright © 2011-2022 走看看