c++右值引用

左值和右值

  C++中所有的值都必然属于左值、右值二者之一。左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束时就不再存在的临时对象

判断方法

有两种方法可以判断是左值还是右值

  1. 可位于赋值号(=)左侧的表达式就是左值;反之,只能位于赋值号右侧的表达式就是右值

  2. 有名称的、可以获取到存储地址的表达式即为左值;反之则是右值。

右值引用

c++11 引入右值引用

右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。

右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面:

消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。能够更简洁明确地定义泛型函数。

性质

和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行初始化

    int a = 10;
//    int && ra = a;   ERROR, 不能用左值初始化右值引用
    int && ra = 20;
    cout << ra << endl; // 输出 20

右值引用可以对值进行修改

int && ra = 20;
ra = 40;
cout << ra << endl; // 输出 40

std::move

  右值引用通过 std::move 函数来实现所有权的转移。std::move 是一个函数模板,将传递的对象转换为右值引用,标记其状态为可移动的。这样,在移动构造函数、移动赋值运算符或其他需要右值引用的操作中,可以利用对象的移动语义来完成高效的资源转移。

  当使用右值引用绑定到一个对象时,编译器会将该对象的类型推断为右值引用类型,并允许在该对象上执行移动操作。通过移动构造函数或移动赋值运算符,可以将资源的所有权从源对象转移到目标对象,通常是通过简单的指针或指针成员的转移。

  移动语义的实现是通过类型的设计和实现来完成的。对于复杂类型,需要定义移动构造函数和移动赋值运算符,并在这些操作中进行资源的所有权转移。对于简单的内置类型,移动语义与拷贝语义相同, 因为内置类型的赋值和拷贝操作是通过简单的按位复制来完成的,不涉及任何资源的动态分配或释放。

  总结起来,右值引用和移动语义的引入使得在 C++ 中可以更高效地实现资源的转移和管理,避免不必要的数据复制和资源分配。这对于提高性能和优化内存使用是非常有益的。

int main() {
    string str("I'm a string");
    string str1 = std::move(str);
    cout << "str: " << str << endl;
    cout << "str1: " << str1 << endl;

    int a = 10;
    int b = std::move(a);
    cout << "a: " << a << endl;
    cout << "b: " << b << endl;
    return 0;
}

输出

str: // str 的值变为空
str1: I'm a string
a: 10 // 简单内置类型,移动语义还是拷贝
b: 10

在 stl 容器中使用,减少深拷贝

string str("I'm a string");

vector<string> vec;
vec.push_back(std::move(str));
cout << "str: " << str << endl; // str 为空
cout << "vec[0]: " << vec[0] << endl;

std::forward

  实现完美转发,std::forward会将输入的参数原封不动地传递到下一个函数中,这个“原封不动”指的是,如果输入的参数是左值,那么传递给下一个函数的参数的也是左值;如果输入的参数是右值,那么传递给下一个函数的参数的也是右值。
  使用完美转发可以减少不必要的拷贝或修改,提高性能。

引用折叠规则

  1. 当一个对象被右值引用(&&)折叠时,结果仍然是右值引用。
    T&& && -> T&&

  2. 当一个对象被左值引用(&)折叠时,结果仍然是左值引用。
    T& & -> T&

  3. 当一个右值引用和一个左值引用折叠时,结果是左值引用。
    T& && -> T&
    T&& & -> T&

example:

A 有两个构造函数,一个参数是左值,一个参数是右值
B 初始化的时候会创建 3 个 A 的对象,根据传入的参数,自动调用 A 对应的构造函数。

struct A
{
    A(int&& n) { std::cout << "rvalue overload, n=" << n << '\n'; }
    A(int& n)  { std::cout << "lvalue overload, n=" << n << '\n'; }
};

class B
{
public:
    template<class T1, class T2, class T3>
    B(T1&& t1, T2&& t2, T3&& t3) :
        a1_{std::forward<T1>(t1)},
        a2_{std::forward<T2>(t2)},
        a3_{std::forward<T3>(t3)}
    {}

private:
    A a1_, a2_, a3_;
};

template<class T, class U>
std::unique_ptr<T> make_unique1(U&& u)
{
    return std::unique_ptr<T>(new T(std::forward<U>(u)));
}

template<class T, class... U>
std::unique_ptr<T> make_unique2(U&&... u)
{
    return std::unique_ptr<T>(new T(std::forward<U>(u)...));
}

auto make_B(auto&&... args) // since C++20
{
    return B(std::forward<decltype(args)>(args)...);
}

int main()
{
    auto p1 = make_unique1<A>(2); // rvalue
    int i = 1;
    auto p2 = make_unique1<A>(i); // lvalue

    std::cout << "B\n";
    auto t = make_unique2<B>(2, i, 3);

    std::cout << "make_B\n";
    B b = make_B(4, i, 5);
}

输出:

rvalue overload, n=2
lvalue overload, n=1
B
rvalue overload, n=2
lvalue overload, n=1
rvalue overload, n=3
make_B
rvalue overload, n=4
lvalue overload, n=1
rvalue overload, n=5
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇