C++ 理解引用绑定

C++ 理解引用绑定,c++,reference,C++,Reference,我们不能将非常量左值引用绑定到右值,但它可以绑定到常量左值引用。我们也不能将右值引用绑定到左值。实际上,标准是这样说的: 8.5.3/5.2: 该引用应为对非易失常数的左值引用 类型(即cv1应为常数),或参考值应为右值参考值 但是有没有比“标准这么说”更好的解释呢?因为它没有语义意义 不能将非常量左值引用绑定到右值,因为修改右值意味着什么?根据定义,其他任何东西都看不到结果,因此这没有意义 int& i = 3; //should change referenced location,

我们不能将非常量左值引用绑定到右值,但它可以绑定到常量左值引用。我们也不能将右值引用绑定到左值。实际上,标准是这样说的:

8.5.3/5.2:

该引用应为对非易失常数的左值引用 类型(即cv1应为常数),或参考值应为右值参考值


但是有没有比“标准这么说”更好的解释呢?

因为它没有语义意义

不能将非常量左值引用绑定到右值,因为修改右值意味着什么?根据定义,其他任何东西都看不到结果,因此这没有意义

int& i = 3;
//should change referenced location, but we aren't referencing a memory location
i = 5; 
不能将右值引用绑定到左值,因为右值引用的存在是为了方便对其引用进行破坏性优化。您不希望您的对象被任意地从下方移出,因此标准不允许这样做

void process_string (std::string&&);
std::string foo = "foo";
//foo could be made garbage without us knowing about it
process_string (foo); 
//this is fine
process_string (std::move(foo));

因为它没有语义意义

不能将非常量左值引用绑定到右值,因为修改右值意味着什么?根据定义,其他任何东西都看不到结果,因此这没有意义

int& i = 3;
//should change referenced location, but we aren't referencing a memory location
i = 5; 
不能将右值引用绑定到左值,因为右值引用的存在是为了方便对其引用进行破坏性优化。您不希望您的对象被任意地从下方移出,因此标准不允许这样做

void process_string (std::string&&);
std::string foo = "foo";
//foo could be made garbage without us knowing about it
process_string (foo); 
//this is fine
process_string (std::move(foo));

考虑一些实际情况:

#include <vector>

void f1( int& i ){i = 1;}
void f2( const int& i ){i = 1;}
void f3( std::vector<int>&& v ){auto v_move{v};}

int main()
{
    f1(3); // error: how can you set the value of "3" to "1"?
    f2(3); // ok, the compiler extend the life of the rvalue "into" f2
    std::vector<int> v{10};
    f3(v); // error: an innocent looking call to f3 would let your v very different from what you would imagine
    f3(std::vector<int>{10}); // ok, nobody cares if the rvalue passed as an argument get modified
}
#包括
void f1(int&i){i=1;}
空f2(常数int&i){i=1;}
void f3(std::vector&&v){auto v_move{v};}
int main()
{
f1(3);//错误:如何将“3”的值设置为“1”?
f2(3);//好的,编译器将右值的寿命延长到f2
std::向量v{10};
f3(v);//错误:对f3的一个看似无辜的调用将使您的v与您想象的非常不同
f3(std::vector{10});//好的,没有人关心作为参数传递的右值是否被修改
}

考虑一些实际情况:

#include <vector>

void f1( int& i ){i = 1;}
void f2( const int& i ){i = 1;}
void f3( std::vector<int>&& v ){auto v_move{v};}

int main()
{
    f1(3); // error: how can you set the value of "3" to "1"?
    f2(3); // ok, the compiler extend the life of the rvalue "into" f2
    std::vector<int> v{10};
    f3(v); // error: an innocent looking call to f3 would let your v very different from what you would imagine
    f3(std::vector<int>{10}); // ok, nobody cares if the rvalue passed as an argument get modified
}
#包括
void f1(int&i){i=1;}
空f2(常数int&i){i=1;}
void f3(std::vector&&v){auto v_move{v};}
int main()
{
f1(3);//错误:如何将“3”的值设置为“1”?
f2(3);//好的,编译器将右值的寿命延长到f2
std::向量v{10};
f3(v);//错误:对f3的一个看似无辜的调用将使您的v与您想象的非常不同
f3(std::vector{10});//好的,没有人关心作为参数传递的右值是否被修改
}

你不能简单地解释一下什么是破坏性优化。。。。还没有面对过这个概念。看看。对它们的解释有点超出了这个问题的范围,但这将帮助您理解我的意思。简短的版本是:移动操作不同于复制操作,它允许执行使其源对象处于“已破坏”状态的操作,而该状态只能被破坏。例如,如果有一个对象内部有指向某些私有数据的指针,那么要复制该对象,必须复制该数据。但要移动它,移动目标可以简单地接管指向存储的指针,并在源对象中留下一个
nullptr
。源上的进一步操作尚未定义(其他成员在这一点上可能没有意义),但析构函数仍然应该正确地清理它。您不能简单地解释一下破坏性优化是什么吗。。。。还没有面对过这个概念。看看。对它们的解释有点超出了这个问题的范围,但这将帮助您理解我的意思。简短的版本是:移动操作不同于复制操作,它允许执行使其源对象处于“已破坏”状态的操作,而该状态只能被破坏。例如,如果有一个对象内部有指向某些私有数据的指针,那么要复制该对象,必须复制该数据。但要移动它,移动目标可以简单地接管指向存储的指针,并在源对象中留下一个
nullptr
。源上的进一步操作尚未定义(此时其他成员可能没有意义),但析构函数仍然应该正确地清理它。