免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 3182 | 回复: 13
打印 上一主题 下一主题

[C++] [结贴]如果const T&是只读引用,那为什么T t(T())是个函数声明而不是对象实例化? [复制链接]

论坛徽章:
2
2015年迎新春徽章
日期:2015-03-04 10:16:532015元宵节徽章
日期:2015-03-06 15:53:22
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-03-04 09:29 |只看该作者 |倒序浏览
本帖最后由 cdsfiui 于 2015-03-04 16:14 编辑

前两天的一个帖子是讨论了:
const T&可以绑定左值,也可以绑定右值(以及字面常量),例如:

  1. void f(const int& i){}
  2. int main(void)
  3. {
  4.     int i = 1;
  5.     f(i);
  6.     f(2);
  7.     const int&r1 = i;
  8.     const int&r2 = 2;
  9.     return 0;
  10. }
复制代码
这都没有问题,那么为什么下面的代码(来自本版另一个帖子)

  1. class X
  2. {
  3. public:
  4.     X(int i){ cout << __FUNCTION__ << endl; }
  5.     X(){ cout << __FUNCTION__ << endl; }
  6.     X(const X&){ cout << __FUNCTION__ << endl; }
  7.     X(X&&){ cout << __FUNCTION__ << endl; }
  8. };
  9. int main(void)
  10. {
  11.     X x(X());
  12.     return 0;
  13. }
复制代码
main函数当中的X x(X())被编译器认为是一个函数声明,而不是一个x变量的实例呢? 如果const X&可以绑定X()这样的临时变量(右值),为什么不是用X的拷贝构造函数,构造了一个x?
之前有一位大侠指出如果写成X x((X())),就是调用了一个默认对象的构造。但是这也不是我期待的:这样改的效果,似乎是一个函数声明加一个默认构造,而没有我期待的拷贝构造。

我希望知道语法上的原因,为什么这里X x(X())是一个函数声明?
非常感谢。。。。。。。。

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:50:28
2 [报告]
发表于 2015-03-04 10:32 |只看该作者
拷贝构造函数的参数是自身类类型的引用吧?

论坛徽章:
2
2015年迎新春徽章
日期:2015-03-04 10:16:532015元宵节徽章
日期:2015-03-06 15:53:22
3 [报告]
发表于 2015-03-04 10:59 |只看该作者
www1862 发表于 2015-03-04 10:32
拷贝构造函数的参数是自身类类型的引用吧?


是啊。
不过,这能帮助解释我的问题么?

论坛徽章:
14
巨蟹座
日期:2013-11-19 14:09:4615-16赛季CBA联赛之青岛
日期:2016-07-05 12:36:0515-16赛季CBA联赛之广东
日期:2016-06-29 11:45:542015亚冠之全北现代
日期:2015-07-22 08:09:472015年辞旧岁徽章
日期:2015-03-03 16:54:15巨蟹座
日期:2014-12-29 08:22:29射手座
日期:2014-12-05 08:20:39狮子座
日期:2014-11-05 12:33:52寅虎
日期:2014-08-13 09:01:31巳蛇
日期:2014-06-16 16:29:52技术图书徽章
日期:2014-04-15 08:44:01天蝎座
日期:2014-03-11 13:06:45
4 [报告]
发表于 2015-03-04 12:03 |只看该作者
为什么这里X x(X())是一个函数声明?
----- x是函数名,返回类型是X,参数是一个省略掉形参名的函数(这个函数的返回类型为X)

对于函数声明 int fun( int param() ) 你能看得懂吗?
再省略形参名后为 int fun( int() ) 你能看得懂吗?
把类型int换成类型X后为 X fun( X() ) 你就看不懂了?

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:50:28
5 [报告]
发表于 2015-03-04 12:25 来自手机 |只看该作者
楼主的意思是 X x();//生成了一个名为x的X对象。
X x( X() );//这里的X()楼主认为是一个无名对象。然后用这个无名对象用拷贝构造的方式生成一个名为x的X对象。

论坛徽章:
14
巨蟹座
日期:2013-11-19 14:09:4615-16赛季CBA联赛之青岛
日期:2016-07-05 12:36:0515-16赛季CBA联赛之广东
日期:2016-06-29 11:45:542015亚冠之全北现代
日期:2015-07-22 08:09:472015年辞旧岁徽章
日期:2015-03-03 16:54:15巨蟹座
日期:2014-12-29 08:22:29射手座
日期:2014-12-05 08:20:39狮子座
日期:2014-11-05 12:33:52寅虎
日期:2014-08-13 09:01:31巳蛇
日期:2014-06-16 16:29:52技术图书徽章
日期:2014-04-15 08:44:01天蝎座
日期:2014-03-11 13:06:45
6 [报告]
发表于 2015-03-04 12:43 |只看该作者
回复 5# www1862
X x();//生成了一个名为x的X对象。
------ 不是吧,这里同样是函数声明,x同样只是个函数名

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
7 [报告]
发表于 2015-03-04 14:43 |只看该作者
lz一共提了两个问题

1. X x(X())被当作声明这是没有办法的事,C++语法在这种地方上存在二义性,X x(X()),你可以解释成一个叫x的函数原型,它返回X类型,接受一个“X()”,这东西其实是一个函数类型,没有参数,返回X。
编译器处理这类东西的原则大致是,如果这个东西可以被解释成声明,那它就是声明,所以它就变成声明了。

2. 在X()外面加一个括号之后,X x((X()))就不是一个有效的函数原型声明,所以它就被解释成“X x(一个默认构造的临时X对象)”,这个构造函数会匹配“X(X&&)”,尽管它可以匹配“X(const X&)”,但C++标准规定临时对象优先匹配右值引用,所以会调用move constructor而不是copy constructor。
rvalue ref本来就是为了这个原因才引入的,也就是说,move/rref之类的东西的引入最初就是为了区分函数参数是一个临时对象(对象的生存期不超过表达式)还是其他情况,其它所有的用法都是围绕这个初衷的完整性补丁。你用临时对象作为参数调用构造函数当然会使用move construtor而不是copy constructor

论坛徽章:
2
2015年迎新春徽章
日期:2015-03-04 10:16:532015元宵节徽章
日期:2015-03-06 15:53:22
8 [报告]
发表于 2015-03-04 15:08 |只看该作者
windoze 发表于 2015-03-04 14:43
lz一共提了两个问题

1. X x(X())被当作声明这是没有办法的事,C++语法在这种地方上存在二义性,X x(X()) ...


谢谢,但是残酷的事实是,以下代码:

  1. #include<iostream>
  2. using namespace std;
  3. class X
  4. {
  5. public:
  6.     X(int i){ cout << __FUNCTION__ << endl; }
  7.     X(){ cout << "default ctor" << endl; }
  8.     X(const X&){ cout << "copy constructor" << endl; }
  9.     X(X&&){ cout << "move constructor" << endl; }
  10. };
  11. int main(void)
  12. {
  13.     X x((X()));
  14.     return 0;
  15. }
复制代码
我在最新的gcc/vc下编译运行,都是打印了"default ctor"。
并没有调用大侠你所期待的move constructor,也没有我所期待的,早构造玩一个临时对象后,还要加上一个copy ctor才算x构造完毕,因为这里是从一个临时对象来构建x,要么是move,要么是copy。

可是,真的只是打印了默认的ctor。

还请大侠继续解释一下!!!!!!

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
9 [报告]
发表于 2015-03-04 15:38 |只看该作者
回复 8# cdsfiui

这是RVO……
C++有一大堆标准框架内的优化方案,你写的代码刚好就是其中的一种情况(什么?你问我为神马随手写段代码就碰上特殊情况?要是不能把你随手写出来的代码优化掉你以为C++标准为神马会复杂的谁也搞不清?)

论坛徽章:
2
2015年迎新春徽章
日期:2015-03-04 10:16:532015元宵节徽章
日期:2015-03-06 15:53:22
10 [报告]
发表于 2015-03-04 16:14 |只看该作者
windoze 发表于 2015-03-04 15:38
回复 8# cdsfiui

这是RVO……


大侠你的回答不但有水平,而且有激情,我很喜欢。结贴啦。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP