2015-09-01 2 views
8

Рассмотрим следующий код: snipetСрок временного объекта, связанный с константной ссылке (метод цепным)

#include <iostream> 

struct S { 
    ~S() { std::cout << "dtor\n"; } 
    const S& f(int i) const { std::cout << i << "\n"; return *this; } 
}; 

int main() { 
    const S& s = S(); 
    s.f(2); 
} 

Output:

2 
dtor 

Т.е. срок жизни объекта продолжается по ссылке, которая объясняется в Herb's article.

Но, если мы изменим только одну строку кода и напиши:

const S& s = S().f(1); 

зову е (2), сделанное на уже уничтоженный объект:

Output:

1 
dtor 
2 

Почему это произошло? Возвращаемое значение f() не является правильным типом «временной»?

+2

'f' возвращает значение lvalue, а не prvalue. –

+0

И время жизни 'S()' заканчивается в конце полного утверждения. – Jarod42

+0

@KerrekSB почему lvalue? Мы не можем поместить 'f' в левой части' = '. – alexolut

ответ

4

Когда вы пишете функцию, таким образом, ...

const S& f(int i) const { std::cout << i << "\n"; return *this; } 

... вы сообщаете компилятор, чтобы вернуть const S& и вы принимаете на себя ответственность за обеспечение ссылочного объекта имеет срок службы, подходящий для вызывающего абонента использовать. («обеспечение» может представлять собой документирование использования клиента, которое правильно работает с вашим дизайном.)

Часто - с типичным разделением кода на заголовки и файлы реализации - реализация f(int) const даже не будет видна вызывающему коду, а в в таких случаях компилятор не знает, в отношении которого S может быть возвращена ссылка, а также то, что это S является временным или нет, поэтому у него нет оснований для принятия решения о необходимости продления срока службы.

Помимо очевидных опций (например, доверенных клиентов для написания безопасного кода, возврата по значению или умного указателя), стоит знать о более неясном варианте ...

const S& f(int i) const & { ...; return *this; } 
const S f(int i) const && { ...; return *this; } 

& и && непосредственно перед перегружать функциональные органы f таким образом, что версия && используется, если *this выполнен с возможностью перемещения, в противном случае используется версия &. Таким образом, кто-то, связывающий const & с номером f(...), вызывается на объект, который истекает, будет привязан к новой копии объекта и продлить срок службы на локальную ссылку const, тогда как когда объект не истечет (пока) ссылка const будет к исходному объекту (который до сих пор не гарантируется вживую до тех пор, пока ссылка - требуется некоторая осторожность).

+0

Есть ли у вас рекомендации по написанию кода более безопасным в случае использования функции, возвращающей ссылку? Может быть всегда присваивается значение (не другая ссылка)? В идеале эта ошибка возникает во время компиляции. – alexolut

+0

@alexolut В настоящее время лучшей рекомендацией является не использование цепочки методов в C++. Также обратите внимание, что порядок оценки неуточнен, поэтому в 'foo.f (bar()) .g (baz())' вы можете увидеть 'baz()' вызываемый до 'bar()'. – Potatoswatter

+0

@alexolut: Я добавил что-то к ответу, обсуждая некоторые варианты. –

3

Почему это произошло? Возвращаемое значение f() не является правильным типом «временности»?

Право, это не так. Это несколько спорный вопрос в последнее время: официальное определение «временности» несколько открыто.

В современных компиляторах временность расширяется. Сначала он применяется только к выражениям prvalue (non-reference), и к ним применяются обращения пользователей («оператор точек»). Теперь это относится и к выражениям выражений и к обращениям к массиву. Хотя вы можете написать операцию перемещения как static_cast< T && >(t), которая сохранит временность, просто написать std::move(t) не будет.

Я работаю над series из proposals, чтобы расширить C++, поэтому ваш пример будет работать так, как вы ожидали. Есть некоторые отличные от нуля шансы, что функция может появиться на C++ 17.