c++临时对象绑定到实参并返回const引用值

C++ temporary object bound to argument and return value const references

本文关键字:返回 const 引用 实参 临时对象 绑定 c++      更新时间:2023-10-16

我无法清楚地知道这是否合法,即使在看了SO的相关问题并阅读了c++ 03标准第192页(http://cs.nyu.edu/courses/fall11/CSCI-GA.2110-003/documents/c++2003std.pdf)之后。

const MyClass& f(const MyClass& arg) {
  return arg;
}
void some_other_function() {
  const MyClass& reference = f(MyClass());
  // Use reference.
}

据我所知,你不能这样做。虽然将临时对象绑定到const引用在c++中是合法的(并且会延长该临时对象的生命周期——参见GOTW 88),但将const ref进一步绑定到另一个const ref并不会延长该临时对象的生命周期。

引用自第192页(c++ 03标准):

函数调用中与引用形参的临时绑定(5.2.2)的完整表达式完成之前一直持续叫

我认为标准非常明确,在// Use reference.之后使用reference是无效的。我修改了你的代码片段来检查它(Mac OS X, clang: Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)):

#include <iostream>
struct MyClass
{
    ~MyClass()
    {
        std::cout << "~MyClass()" << std::endl;
    }
};
const MyClass& f(const MyClass& arg) {
    std::cout << "f()" << std::endl;
    return arg;
}
int main() {
    const MyClass& reference = f(MyClass());
    std::cout << "main()" << std::endl;
}

输出:

f()
~MyClass()
main()

换句话说,除非我和clang开发人员都误解了c++标准,否则它是非法的。

我无法从标准中得到一个明确的解释,所以我决定检查一下事实上的标准是什么。以下代码:

#include <cstdio>
struct MyClass
{
    MyClass() { printf("constructorn"); }
    ~MyClass() { printf("destructorn");  }
    MyClass(const MyClass&) { printf("copyn"); }
    MyClass(MyClass&&) { printf("moven"); }
};
const MyClass& f(const MyClass& arg) {
    return arg;
}
int main()
{
    {
        printf("before constructionn");
        const MyClass& reference = f(MyClass());
        printf("after constructionn");   
    }
    printf("outside scopen");
}

收益率:

before construction
constructor
destructor
after construction
outside scope

适用于MSVC、clang和g++。根据我们的主要编译器供应商,这似乎是不合法的。

这个问题类似于下面的问题:到std::map

的操作符[]

下面的代码解释了到底发生了什么

#include <iostream>

struct MyClass{
  int member;

  MyClass():member(0){
      std::cout<<"MyClass ctr "<<std::endl;
  }
  MyClass(const MyClass& rhs){
      std::cout<<"MyClass copy ctr "<<std::endl;
  }
  ~MyClass(){
      std::cout<<"MyClass dtr"<<std::endl;
      member = -1;
  }
};
void f2(const MyClass& obj){
    std::cout<<"func "<<obj.member<<std::endl;
}   
const MyClass& f3(){ 
    return MyClass(); 
}
MyClass f4(){ 
    return MyClass(); //ideally not a good idea, exception is
    //http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
}
int main()
{
    std::cout << "-----Faulty Case-----------"<<std::endl;
    //reference returned by f3 is local to f3 call and 
    //is destructed as soon as f3() is out of stack
    //and hence the reference in f2() is not valid
    f2( f3() );
    std::cout <<std::endl<< "-----Correct way-----------"<<std::endl;
    //A temporary object is returned by f4 which is then referred by reference in f2.
    //This reference is alive in stack of f2 and hence can be used inside 
    //f2 with valid results.
    //As explained in following article, the refernce should remain
    //alive in stack to use temporary objects.
    //http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
    f2( f4() );
    //note in previous expression, f4 returns by value but still copy ctr is not invoked,
    //this I believe is Return Value Optimization (might be compiler dependent)
    return 0;
}

这个程序的输出将是:

Executing the program....
$demo 
-----Faulty Case-----------
MyClass ctr 
MyClass dtr
func -1
-----Correct way-----------
MyClass ctr 
func 0
MyClass dtr