Мы не можем написать int& ref = 40
, потому что нам нужно lvalue
с правой стороны. Но мы можем написать const int& ref = 40
. Почему это возможно? 40 вместо rvalue
rvalue
Я знаю, что это исключение, но почему?
Мы не можем написать int& ref = 40
, потому что нам нужно lvalue
с правой стороны. Но мы можем написать const int& ref = 40
. Почему это возможно? 40 вместо rvalue
rvalue
Я знаю, что это исключение, но почему?
Как говорит Страуступ:
Инициализатор для const T & не должно быть lvalue или даже типа Т. В таких случаях:
[1] Во-первых, при необходимости применяется неявное преобразование типа в T.
[2] Затем полученное значение помещается во временную переменную тип T.
[3] Наконец, эта временная переменная используется как значение инициализатор.
Итак, при вводе const int& ref = 40
временная переменная int создается за кулисами, а ref привязан к этой временной переменной.
В языке есть правило, которое позволяет привязывать ссылку const lvalue к rvalue. Основная причина этого правила заключается в том, что если его нет, тогда вам придется предоставлять разные перегрузки функций, чтобы иметь возможность использовать временные аргументы в качестве аргументов:
class T; // defined somewhere
T f();
void g(T const &x);
Используя это правило, вы можете сделать g(f())
без него, чтобы иметь возможность сделать это, вам придется создать другую перегрузку g
, которая принимает значение rvalue (и это время, когда rvalue-ссылки даже не были на языке!)
Почему это возможно?
40 является буквальным здесь. Константные ссылки могут быть инициализированы литералами и временными данными для продления срока их службы. Это можно сделать с помощью компилятора:
int const& ans = 40;
// transformed:
int __internal_unique_name = 40;
int const& ans = __internal_unique_name;
Другая ситуация - когда у вас есть функция, например:
void f( std::string const& s);
и вы хотите называть его
f( "something");
Эта временная переменная может быть привязана только к константной ссылке.
Вы можете привязать значение r к константной ссылке. Язык гарантирует, что связанный объект будет проживать до тех пор, пока область действия ссылки не закончится и даже не вызовет правильного деструктора статически. Это, например, используемый в реализации ScopeGuard (http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758?pgno=2), чтобы иметь поведение виртуального деструктора, не платя за вызов виртуального метода.