1. То что ссылку нельзя инициализировать значением вместо другой переменной это понятно и логично, но как получается что если указать ссылку как const, то её становится возможным инициализировать значением?
const std::string& s = "String";
2. Если вернуть переменную из функции, то почему в этом-же месте этой переменной нельзя присвоить значение? Как получается что возвращение переменной по ссылке даёт возможность присваивать значения возвращённым переменным?
#include <iostream>
#include <string>
std::string f(std::string& s)
{
return s;
}
int main()
{
std::string s = "string";
f(s) = "another string";
std::cout << s << std::endl;
}
Верхний код в месте использования функции вообще должен выглядеть подобным образом, s = "another string";
, но вывод "string" вместо "another string" для меня выглядит странно. Не могли бы вы разъяснить для меня эти тонкости?
Присваивать значение можно, но не ссылке, а переменной, на которую она ссылается.
Ссылка без const
ссылается на изменяемый объект, соответственно, это должна быть переменная или нечто подобное. При попытке инициализировать ссылку при помощи rvalue происходит ошибка компиляции. Константная же ссылка вполне может ссылаться на неизменяемый объект, каким и считается rvalue-значение. Ещё можно было бы использовать специальную rvalue-ссылку: std::string &&s = "String";
.
Оно ничего не меняет. Было можно - и осталось можно, было нельзя - и осталось нельзя.
Для того, чтобы было "another string"
, ты потерял возврат по ссылке:
std::string &f(std::string& s)
// ^-------------------- в твоём коде этого нет
То что ссылке нельзя присваивать значение это понятно и логично, но как получается что если указать ссылку как const, то ей становиться возможным присваивать значение?
Потому что "String"
— это массив const char[]
, а ваша ссылка требует std::string
. Из-за несоответствия типов компилятор не может напрямую выполнить присвоение, а вместо этого генерирует создание экземпляра std::string
с вызовом конструктора std::string::string(const char*)
, ибо нетривиальный тип.
И тут встаёт нехороший вопрос. Ссылка ничего в себе не хранит, она указывает на другой объект в другой переменной. А переменной-то и нету. И где тогда хранить этот std::string
? А хранить надо явно, ведь его можно впоследствии менять через ссылку.
Но как только вы делаете ссылку константной, ...
А вот и нет. Причина банальна — комитет сделал исключение из правил:
Normally, a temporary object lasts only until the end of the full expression in which it appears. However, C++ deliberately specifies that binding a temporary object to a reference to const
on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error.
Перевод:
Как правило, временный объект живёт до окончания выражения, в котором был создан. Однако, C++ осознанно указывает, что присваивание временного объекта ссылке на const
продлевает время жизни объекта до времени жизни этой ссылки, избегая таким образом проблемы «висячих ссылок».
Если вернуть переменную из функции, то почему в этом-же месте этой переменной нельзя присвоить значение?
Потому что функция вернула копию объекта. Вы можете делать с ним всё, что угодно, но как только действие строки завершится, эта копия будент уничтожена, если не продлить время её жизни посредством присваивания в переменную.
Как получается что возвращение переменной по ссылке даёт возможность присваивать значения возвращённым переменным?
Ну, так это ссылка, то есть тот же указатель. Ссылка указывает на оригинальный объект, давая возможность см ним орудовать.
Ссылкам в С++ не только можно, но часто бывает нужно присваивать значения. При этом нужно понимать, что присваивая новое значение ссылке, вы фактически присваиваете новое значение той переменной, на которую эта ссылка ссылается:
int a = 1;
int& b = a; // b - ссылка на a
b = 2; // здесь изменится значение a
cout << a << endl; // выведет 2!
Передача аргументов в функцию по ссылке преследует 2 цели:
Избежать создание копии переменной (если переменная - тяжелый объект с "дорогим" копированием). Если при этом не предполагается изменение этой переменной внутри функции, ее надо передавать по константной ссылке.
Если переменную действительно необходимо изменить внутри функции. В этом случае, использовать константную ссылку нельзя:
int foo(const int& a, int& b string& failure_reason)
{
...
b++;
a++; // ошибка, не компилируется
...
if (something_wrong) {
failure_reason = "why error occured"
return 1
} else {
failure_reason = "";
return 0
}
}
...
int a = 1, b = 2;
string s;
if (foo(a, b, s)) {
cout << "error: " << s << endl;
}
cout << b << endl; // 3</pre>
При возврате переменной из функции по ссылке, ей как раз можно присваивать значение:
int& foo() {
static int a = 0;
return a;
}
foo() = 1;
cout << foo() << endl;
Обратите внимание, что возвращать из функции ссылку на локальную переменную - неопределенное поведение, т.к. при выходе из функции (области видимости) переменная уничтожается и ваша ссылка будет ссылаться на удаленный объект:
int& foo() {
int a = 0;
return a; // скомпилируется, но работать не будет
}
Стоит задача: Создать хеш-таблицу со случайными ключами и удалить из нее записи с ключами из диапазона min<key<max
Есть ли возможность контроллировать статус выполнения операций вывода в С++ без try - catch блоков?