为什么右值无法绑定到non-const引用

chinesealbert 2009-08-27 04:13:41
以下是读书的原文:

====================================================================================================

T a(v);
这个语句显式用v作为参数调用T的某个最适合的可单参数调用的ctor来构造a
该语句形式被C++标准称为direct-initialization
这里强调两点,一个是显式,这说明这种方式构造a的话可以调用被explicit声明的ctor
第二点是最适合,这是由overload rules决定的,而不是那么想当然,示例代码如下
struct T
{
T(){}
T(int){}
operator int(){return 0;}
private:
T(T&){}
};
T foo() {return T();}
int main()
{
T a;

T b(a); // 编译出错,最适合的T(T&)不可访问,为private

T c(foo());// 编译成功,foo()返回的临时对象是rvalue,不能绑定到non-const引用
// 因此T(T&)不是由overload rules选定的最适合ctor
// 但是rvalue可以调用一次自定义隐式转换cast到int然后调用T(int)构造c
// 也就是说,overload rules选择了T(int)

return 0;
}
这段代码说明,即使类型相同,调用的也不一定是copy ctor
如果有人怀疑T(T&)不是copy ctor,认为T(const T&)才是,请参阅C++标准12.8
====================================================================================================


这里我不明白这一句话:“T c(foo());// 编译成功,foo()返回的临时对象是rvalue,不能绑定到non-const引用”
为什么?

...全文
213 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
光宇广贞 2009-08-27
  • 打赏
  • 举报
回复
对了,好像我忘了说为何 non-const 本质上是“左值语义”,这个,多说一句便是……你不是可以“指名道姓”地“引用”它并修改值么?所以是左值的。
光宇广贞 2009-08-27
  • 打赏
  • 举报
回复
右移在语义上是这样的,基本上简单地说,就是“不具名,不对应内存块”,比如常数可以做为右值,临时对像也可以做为右值,右值具有“移动语义”,就是我把我的值移动给你,然后我自身可能就消失掉了,比如常数给变量赋值,和临时对像给实对像的赋值过程……

T& 这个,就是 non-const 本质上是“左值”语义的,左值与右值对应,就是“具名,且对应内存”,左值变量是可以访问的,因为它对应内存,因故可以将左值放到等号左边。

语义上明晰的话,那么为何临时对像不能给 non-const 就好解释了。左值可以做右值,右值不可以做左值。在 C++ 98/03 现行标准下,右值只可以传参给 const 形参,而绝对不可以给 non-const 形参,而在新标准 C++ 09 里面,对左值与右值做了更为严格和明晰地区别,楼主可以关注一下。
taodm 2009-08-27
  • 打赏
  • 举报
回复
哦,那你继续。
chinesealbert 2009-08-27
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 taodm 的回复:]
13楼的代码你真的编译过?
[/Quote]

没有呀。。。。 我没钱买计算机,所以就只能来这里发问题了,难道你把我忘记了?
晨星 2009-08-27
  • 打赏
  • 举报
回复
应该这么说:
const T& t = foo();
是可以的。
T& t = foo();
不可以。
taodm 2009-08-27
  • 打赏
  • 举报
回复
13楼的代码你真的编译过?
晨星 2009-08-27
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 chinesealbert 的回复:]
int main()
{
  T a = foo(); //通过
  const T &b = a; //通过!!
  T &c = foo(); //不通过,理由如上,对否??
}
[/Quote]
第一个俺就不评论了,毕竟你的构造函数是私有的,而且参数还是非常量的引用。
后两个没错,就是这样,呵呵。
chinesealbert 2009-08-27
  • 打赏
  • 举报
回复
刚才12楼少写一个const,请直接看13楼
chinesealbert 2009-08-27
  • 打赏
  • 举报
回复
我懂了,我懂了:

struct T
{
T(){}
T(int){}
operator int(){return 0;}
private:
T(T&){}
};
T foo() {return T();}
int main()
{
T a = foo(); //通过
const T &b = a; //通过!!
const T &c = foo(); //不通过,理由如上,对否??
}
chinesealbert 2009-08-27
  • 打赏
  • 举报
回复
我懂了,我懂了:

struct T
{
T(){}
T(int){}
operator int(){return 0;}
private:
T(T&){}
};
T foo() {return T();}
int main()
{
T a = foo(); //通过
const T &b = a; //通过!!
T &c = foo(); //不通过,理由如上,对否??
}


晨星 2009-08-27
  • 打赏
  • 举报
回复
左右值的详细区分你自己查资料吧。
但如果不想研究的很细,基本上可以这么认为:能放在赋值号左边的都是左值,否则,八成是右值。

像你定义的那个foo()函数,你调用它所返回的那个临时的对象就是右值,因此,你写
T t = foo();
有可能成功,但写:
T t;
foo() = t;
肯定不行。
taodm 2009-08-27
  • 打赏
  • 举报
回复
抱歉,a是左值,不可修改的左值。
所以,真的不要去钻“左值、右值”的牛角尖了。
pengzhixi 2009-08-27
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 gongxinheng 的回复:]
只能在等号(赋值符号)右边使用的值叫右值(一般是常量或字面值)
5 = a; // 5肯定是右值,因为放在=号左边不能用
"sjdl" // 这个也是右值,字符串常量
[/Quote]

只能在等号右边使用的值叫右值。这应该是以前的 说法了 。
chinesealbert 2009-08-27
  • 打赏
  • 举报
回复
那就这样吧,简单的、按惯例的,什么是左值,什么是右值?
变量就是通常所说的左值? 常量 或者 没有名字的量 通常就是右值?

如:
int t = 0;

t就是一个左值,对吧?
0就是一个右值,对吧?

const int a = 0;
a也应该是个右值,对吧?
HengStar 2009-08-27
  • 打赏
  • 举报
回复
只能在等号(赋值符号)右边使用的值叫右值(一般是常量或字面值)
5 = a; // 5肯定是右值,因为放在=号左边不能用
"sjdl" // 这个也是右值,字符串常量
taodm 2009-08-27
  • 打赏
  • 举报
回复
呃,那你就别讨论了,这是超级大坑了。要请 卫亭 大神才行的话题。
chinesealbert 2009-08-27
  • 打赏
  • 举报
回复
多谢上边几位的闪电速度般地答案!!


那我这样问吧,到底啥是右值??

HengStar 2009-08-27
  • 打赏
  • 举报
回复
举个简单的例子
看代码

void func( int& a )
{
a = 10; // 给传进来的a赋值
}

func( 5 ); // 这样调用肯定是不通过编译的,因为5是常量(右值),不能给它赋值
晨星 2009-08-27
  • 打赏
  • 举报
回复
右值本来就不能绑定到non-const引用啊?这是基本语法规则。
楼主是问语言为啥这么规定吗?那也不难理解啊。非常量引用常常会修改被引用的对象,而允许一个右值老是被改来改去的,那不是很别扭么?
taodm 2009-08-27
  • 打赏
  • 举报
回复
因为“规定”
加载更多回复(1)
学习并掌握C++2.0(11+14+17+20)的新特性,学习线程及线程池的应用 ---------------------------------------------------给小白学员的3年学习路径及计划技术方面分三块:1.纯开发技术方向2.音视频流媒体专业方向3.项目实战---------------------------------------------------1.纯开发技术方向(1) C++必须要过硬(至少学会10本经典好书)(2) 系统级编程(Windows、Linux),必须特别熟练系统API,灵活运用(3) 框架与工具(Qt、MFC):必须精通其中一种。(4) 架构与设计模式:需要提升一个高度,不再是简单的编码,而是思维模式。(5) 驱动级别(如果有兴趣,可以深入到驱动级:包括Windows、Linux)(6) 最好学习点Java+Html+javascript等WEB技术。2.音视频流媒体专业方向(1) 音视频流媒体基础理论:   必须认真学会,否则看代码就是看天书(2) 编解码方向:精通h.264,h.265(hevc), 包括理论和各个开源库(ffmpeg,libx264,libx265,...)。(3) 直播方向:  精通各种直播协议(rtsp,rtmp,hls,http-flv,...), 钻研各个开源库(live555,darwin,srs,zlmediakit,crtmpserver,...)(4) 视频监控:  理论+开源库(onvif+281818)(EasyMonitor、iSpy、ZoneMinder(web)、...) 3.项目实战(1) Qt项目:  至少要亲手练习10个实战项目(网络服务器、多线程、数据库、图像处理、多人聊天、等等)(2)音视频项目:包括编解码、视频监控、直播等各个方向,都需要亲手实战项目,包括视频服务器、后台管理系统、前端播放器(多端)---------------------------------------------------  第1章 C++11新特性 41). nullptr关键字与新语法 42). auto和decltype类型推导 6 auto讲解 6 auto示例 7 decltype 83). for区间迭代 94). 初始化列表 105). 模板增强 11外部模板 11类型别名模板 12默认模板参数 126). 构造函数 13委托构造 13继承构造 147). Lambda 表达式 158). 新增容器 20std::array 20std::forward_list 21无序容器 22元组 std::tuple 239). 正则表达式 2610). 语言级线程支持 28多线程库简介 2811). 右值引用和move语义 31右值引用和move语义 32转移左值 3412). constexpr 35第2章 C++14新特性 36Lambda 函数 36类型推导 37返回值类型推导(Return type deduction) 37泛型lambda 39[[弃用的]]  [[deprecated]]属性 40二进制数字和数字分隔符 41第3章 C++17新特性 42安装GCC10.2 42安装msys2-x86_64-20200720 42更新镜像 42更新软件库 43安装 MinGW64 等必要的软件 43环境变量Path 43编译命令 43constexpr 44typename 45折叠表达式 47结构化绑定 48条件分支语句初始化 49聚合初始化 50嵌套命名空间 52lambda表达式捕获*this的值 53改写/继承构造函数 54用auto作为非类型模板参数 55__has_include 56fallthrough 57nodiscard 57maybe_unused 58第4章 C++20新特性 59编译命令 59concept 59typename 60explicit 61constinit 62位域变量的默认成员初始化 62指定初始化 63基于范围的for循环初始化 64放宽基于范围的for循环,新增自定义范围方法 65嵌套内联命名空间 66允许用圆括弧的值进行聚合初始化 67unicode字符串字面量 68允许转换成未知边界的数组 68likely和unlikely 69第5章 C++2.0(11/14/17/20)总结与分析 705.1 C语言与C++ 715.2 语言可用性的强化 725.2.1 常量 725.2.2 变量及其初始化 735.2.3 类型推导 745.2.4 控制流 765.2.5 模板 775.2.6 面向对象 815.3 语言运行期的强化 835.3.1 Lambda 表达式 835.3.2 右值引用 865.4 容器 885.4.1 线性容器 885.4.2 无序容器 895.4.3 元组 895.5 智能指针与内存管理 905.5.1 RAII 与引用计数 905.5.2 std::shared_ptr 905.5.3 std::unique_ptr 915.5.4 std::weak_ptr 91第6章 C++2.0多线程原理与实战 93什么是并发 93并发的方式 93为什么使用并发 95线程简介 96创建线程的三种方式 971. 通过函数 972.通过类对象创建线程 993.通过lambda表达式创建线程 101thread线程的使用 101互斥量与临界区 105期物Future 111条件变量 112原子操作 114内存模型 118第7章 C++2.0线程池原理与实战 120线程与线程池的基本原理 1201)、线程 1202)、线程的生命周期 1213)、什么是单线程和多线程 1214)、线程池 1225)、四种常见的线程池 123线程池的架构与流程 123线程池代码实战 125    

64,651

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧