Является ли возврат по значению всегда постоянным?

Этот код не компилируется:

class C {};

void foo (C& c) {}

C bar() { return C(); }

int main()              
{
  foo(bar());
}               

Ошибка компиляции (GCC 4.1.2) в строке foo(bar()):

неверная инициализация неконстантной ссылки типа "C&" из временного типа "C"

Как bar() возвращает mutable объект, он должен скомпилировать...
Почему C++ не позволяет этот код выше?


РЕДАКТИРОВАТЬ: я суммирую в ответе ниже все хорошие идеи из всех ответов;-)

8 ответов

Решение

Здесь применимо правило, согласно которому вы не можете создать неконстантную ссылку на временный объект. Если foo был объявлен как foo(const C&) код будет в порядке.

Однако сам временный объект не является константой; вы можете вызывать неконстантные функции-члены, например, bar().non_const_member_function(),

В C++11 можно написать foo, чтобы получить ссылку на rvalue; в этом случае вызов будет в порядке:

void foo(C&&);
foo(bar());  // okay

Это потому, что значение, возвращаемое bar это временное значение. Поскольку его существование является временным, вы не можете использовать указатель или ссылку на него.

Однако, если вы храните копию этого временного объекта, как при втором изменении, вы больше не передаете ссылку на временный объект в foo, но ссылка на реальный материальный объект. И в первом случае, когда вы переходите к ссылке на константный объект, компилятор обеспечивает, чтобы временный объект оставался достаточно долго (согласно спецификации C++).

Вопрос не с декларацией bar но с этим из foo, foo принимает неconst ссылка, и временные могут связываться только с const ссылки (что затем продлевает время жизни временного объекта, чтобы соответствовать времени ссылки, с которым оно связано).

Разрешение неconst Ссылка на временную привязку не имеет большого смысла. Неconst ссылка подразумевает, что он будет изменять любой объект, связанный с ним. Модификация временного не имеет смысла, так как его срок службы ограничен, и изменения будут потеряны, как только он выйдет из области видимости.

Изменяемые (lvalue-) ссылки не привязываются к временным значениям. Однако const-ссылки связываются с временными значениями. Это не имеет никакого отношения к тому, является ли объект, возвращаемый значением, постоянным или нет; это просто вопрос того, является ли выражение временным или нет.

Например, действует следующее:

struct C { void i_am_non_const() {} };

int main()
{
    bar().i_am_non_const();
}

Это выбор дизайна. Здесь нет ничего невозможного. Просто выбор дизайна.

В C++11 у вас есть третий вариант, который также является лучшим вариантом:

void foo(C && c) {} 

То есть используйте rvalue-ссылки.

Это не const, но это временное значение. Таким образом, он не может связываться с неконстантной ссылкой на lvalue.

Он может привязываться к ссылке наконстанту или r-значение, и вы можете вызывать функции-члены (константные или нет) для нее:

class C { void f(); };

void foo_const(C const &);
void foo_rvalue(C &&);

foo_const( bar() );  // OK
foo_rvalue( bar() ); // OK
bar().f();           // OK

Спасибо всем за ваши ответы:-)
Здесь я собираю ваши хорошие идеи;-)

Ответ

Возврат по значению не const, Например, мы можем вызвать неконстантные функции-члены return по значению:

class C { 
  public:
    int x;
    void set (int n) { x = n; }  // non-const function
};

C bar()  { return C(); }

int main ()
{
    bar.set(5); // OK
}

Но C++ не допускает неконстантных ссылок на временные объекты.
Однако C++11 допускает неконстантные rvalue-ссылки на временные объекты.;-)

объяснение

class C {};

void foo (C& c) {}

C bar()  { return C(); }
//bar() returns a temporary object
//temporary objects cannot be non-const referenced

int main()
{
  //foo() wants a mutable reference (i.e. non-const)
  foo( bar() ); // => compilation error     
}                 

Три исправления

  1. + Изменить foo декларация

      void foo (const C& c) {}
    
  2. Использовать другой объект

      int main()              
      {
        C c;
        foo( c = bar() );
      }
    
  3. Используйте C++11 rvalue-reference

      void foo(C && c) {}
    

более того

Чтобы подтвердить, что временные объекты являются постоянными, приведенный выше исходный код не работает по той же причине:

class C {};

void foo(C& c) {}

int main()                
{
  foo( C() );
}            

Настоящая, суровая правда заключается в том, что нет смысла ссылаться на временное значение.

Смысл передачи объекта по ссылке заключается в том, что он позволяет изменять его состояние. Однако в случае с временным по самой своей природе было бы не очень полезно иметь возможность изменять его, поскольку у вас нет способа получить другую ссылку на него позже в вашем коде, чтобы увидеть изменения.

Тем не менее, это несколько иначе, если у вас есть const ссылка. Поскольку вы будете когда-либо читать только с const ссылка, имеет смысл иметь возможность использовать временные там. Вот почему компилятор "взломает" его и предоставит более постоянный адрес временным файлам, в которые вы хотите "превратиться". const Рекомендации.

Итак, правило таково, что вы не можете получитьconst ссылка на временное значение. (Это немного изменилось в C++11, где у нас есть новый тип ссылок, которые служат именно этой цели, но методы должны работать с ними особым образом.)

Другие вопросы по тегам