Временной объект С++, привязанный к аргументу и возвращаемому значению const ссылки

Я не могу получить четкое представление о том, является ли это законным, даже после рассмотрения связанных вопросов по SO и чтения стандартной страницы С++ 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.
}

Мне кажется, что это так.

Ответы

Ответ 1

Насколько я знаю, вы не можете этого сделать. Хотя привязка временной ссылки к const является законной С++ (и удлиняет время жизни этого временного - см. GOTW 88), дальнейшее связывание константы ref к другому const ref не удлиняет время жизни этого временного.

Цитата со страницы 192 (С++ 03):

Временная привязка к эталонному параметру в вызове функции (5.2.2) сохраняется до завершения полного выражения, содержащего вызов

Я думаю, что стандарт довольно явный, что использование ссылки после // Use 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 неправильно интерпретируют стандарт С++, это незаконно.

Ответ 2

Мне не удалось получить четкую интерпретацию из стандарта, поэтому я решил проверить, что такое де-факто стандарт. Следующий код:

#include <cstdio>

struct MyClass
{
    MyClass() { printf("constructor\n"); }
    ~MyClass() { printf("destructor\n");  }
    MyClass(const MyClass&) { printf("copy\n"); }
    MyClass(MyClass&&) { printf("move\n"); }
};

const MyClass& f(const MyClass& arg) {
    return arg;
}

int main()
{
    {
        printf("before construction\n");
        const MyClass& reference = f(MyClass());
        printf("after construction\n");   
    }
    printf("outside scope\n");
}

Урожайность:

before construction
constructor
destructor
after construction
outside scope

Для MSVC, clang и g++. Кажется, что это не является законным в соответствии с нашими основными поставщиками компилятора.

Ответ 3

Этот вопрос похож на следующий вопрос: Pass const Key_Type & к оператору [] 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