C++11引用临时变量的终极解析

发表于:2015-8-28 10:19

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:compilerTech    来源:51Testing软件测试网采编

  工作中遇到一个引用临时变量的问题,经过两天的学习,私以为:不仅弄明白了这个问题,还有些自己的独到见解。
  这里使用一个简单的例子来把自己的学习过程和理解献给大家,如果有什么问题请不吝指正。
  *************************Code*************************
  class Dog
  {
  public:
  Dog(){}
  virtual ~Dog(){}
  };
  void NonConstReference (Dog & dog )
  {
  //tell the dog to do something here
  }
  void TestNonConstReference ()
  {
  NonConstReference( Dog());
  }
  *************************VS 2013, Level4 (/W4)*************************
  warning C4239: nonstandard extension used : 'argument' : conversion from 'Dog' to 'Dog &'
  *************************GCC, C++11*************************
  -------------- Build: Debug in Test (compiler: GNU GCC Compiler)---------------
  mingw32-g++.exe -Wall -fexceptions -g -std=c++11  -c G:\MyBackup\code\CodeBlock\Test\main.cpp -o obj\Debug\main.o
  G:\MyBackup\code\CodeBlock\Test\main.cpp: In function 'void TestNonConstReference()':
  G:\MyBackup\code\CodeBlock\Test\main.cpp:18:29: error: invalid initialization of non-const reference of type 'Dog&' from an rvalue of type 'Dog'
  G:\MyBackup\code\CodeBlock\Test\main.cpp:11:6: error: in passing argument 1 of 'void NonConstReference(Dog&)'
  Process terminated with status 1 (0 minute(s), 0 second(s))
  2 error(s), 0 warning(s) (0 minute(s), 0 second(s))
  *************************lvalue, xvalue, prvalue的一般定义*************************
  首先lvalue, rvalue 都是针对表达式的;任何一个表达式都可以按照如下归类方式归类:
  lvalue指代一个函数或者对象。例如:
  E是指针,则*E是lvalue
  一个函数的返回值是左值引用,其返回值是lvalue。例如int& foo();
  xvalue指代一个对象,但是和lvalue不同,这个对象即将消亡。
  prvalue指代一个临时对象、一个临时对象的子对象或者一个没有分配给任何对象的值。例如:
  一个函数的返回值是平常类型,其返回值是rvalue。例如int foo();
  没有分配给任何对象的值。如5.3,true。
  *************************lvalue, xvalue, prvalue的区分*************************
  说明:这部分来自C++ PROGRAMMING LANGUAGE 4TH EDTION。
  There are two properties that matter for an object when it comes to addressing, copying, and moving:
  ? Has identity: The program has the name of, pointer to, or reference to the object so that it is possible to determine if two objects are the same, whether the value of the object has changed, etc.
  ? Movable: The object may be moved from (i.e., we are allowed to move its value to another location and leave the object in a valid but unspecified state, rather than copying;).
  It turns out that three of the four possible combinations of those two properties are needed to precisely describe the C++ language rules (we have no need for objects that do not have identity and
  cannot be moved).
  Using ‘‘m for movable’’ and ‘‘i for has identity,’’ we can represent this classification of expressions graphically:
  So, a classical lvalue is something that has identity and cannot be moved (because we could examine it after a move), and
  a classical rvalue is anything that we are allowed to move from.
  *************************ISO IEC 14882 2011 8.5.3 References*************************
  ISO文档使用cv来代表const volatile 修饰符。
  并且假设我们使用这样的一种方式来赋值:cv1 T1 dest = cv2 T2 src;
  举个例子就是:
  int src = 123;
  const int& dest = src;
  void function(const int& dest){};
  function(src);
  ISO文档首先给出了两个概念:reference-related, reference-compatible。
  Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if
  T1 is the same type as T2, or
  T1 is a base class of T2.
  “cv1 T1” is reference-compatible with “cv2 T2” if
  T1 is reference-related to T2 and
  cv1 is the same cv-qualification as, or greater cv-qualification than, cv2.
  说明:cv1 >= cv2的情况都有哪些呢:const > 没有修饰符, const volatile > const,etc.
  分析一次赋值:cv1 T1 dest = cv2 T2 src; 是否合法采用如下4个步骤:
  1.如果dest 是一个lvalue reference,同时:
  1.1如果src是一个左值(不是一个bit-filed),并且cv1 T1 是 reference-compatible with cv2 T2的;
  1.2如果T2是一个类类型(class, struct, union, etc.),即使cv1 T1 不是 reference-compatible with cv2 T2的,只要cv2 T2可以被转换成cv3 T3类型的一个左值(src1),这时如果cv1 T1 是 reference-compatible with cv3 T3的;
  那么,dest 就帮定到src,或者src1上。
  2.如果cv2 T2 src不能满足1.1,1.2,那么cv1 就应该是一个包含const的lvalue reference定义,否则它就因该是一个rvalue reference。此时如果cv2 T2满足如下条件:
  2.1如果src是一个xvalue, 类类型的prvalue, array prvalue 或者返回左值的函数,并且cv1 T1 是 reference-compatible with cv2 T2的;
  2.2如果cv2 T2是类类型的,即使cv1 T1 不是 reference-compatible with cv2 T2的,只要cv2 T2可以被转换成cv3 T3类型的一个2.1规定的值,假设是src1;
  那么,dest就帮定到src,或者src1上。
  3.如果cv2 T2 src也不能满足2.1,2.2,那么编译器就为src创建一个临时变量。
  3.1创建此临时变量的条件是:cv1 T1 是 reference-related with cv2 T2,并且cv1 >= cv2;
  4.如果cv2 T2 src不能满足上面所有的条件,那么cv1 T1就应该是一个rvalue reference。此时,如果cv2 T2是一个lvalue的话,编译器应该抱错。
  *************************Reference 匹配(过滤)过程*************************
  **************************************这里有些例子**************************************
  -------------------------------能被规则1处理完毕-------------------------------------------------
  double d = 2.0;
  double& rd = d; //d, is an lvalue, and the cv1 equals cv2, 1.1能够处理
  const double& rcd = d; // d, is an lvalue,
  // the cv1 >= cv2: const > 没有修饰符,1.1能够处理
  struct A { };
  struct B : A
  {
  operator int&();
  } b;
  A& ra = b; // b, has a class type: struct;
  //cv1 is reference related with cv2, ra is the base class of the b,1.2能够处理
  const A& rca = b; // b, has a class type, struct;
  //cv1 is reference related with cv2, ra is the base class of the b;
  // the cv1 >= cv2: const > 没有修饰符,1.2能够处理
  int& ir = B(); // B(), has a class type: struct
  //it can be converted to an lvalue of the int&: operator int&()
  //cv1 == cv2: cv修饰符都是空,1.2能够处理
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号