Как передать ссылки по значению в C++?
Я пытаюсь создать неизменный тип (класс) в C++,
Я сделал это так, чтобы все методы "aka member functions" не модифицировали объект и вместо этого возвращали новый экземпляр.
Я сталкиваюсь с множеством проблем, но все они вращаются вокруг ссылочных типов в C++.
Одним из примеров является передача параметров одного и того же типа класса по ссылке:
Imm Imm::someOp( const Imm& p_im ) const
{
...
p_im = p_im.someOtherOp(); //error, p_im is const, can't modify it!
...
}
Ошибка вызвана передачей значения по ссылке. Если бы вместо этого я передавал ссылку по значению, то строка ошибки выше не была бы ошибкой!
Рассмотрим пример Java/C#
class Imm
{
...
Imm someOp( Imm p_im )
{
....
p_im = p_im.someOtherOp(); //ok, you're not modifying the
//original object, just changing the local reference
....
}
....
}
Как я могу сделать что-то подобное в C++? Я знаю, что могу использовать указатели, но потом я сталкиваюсь со всей неразберихой в управлении памятью. Я не хочу беспокоиться о том, кто владеет ссылками на объекты.
В идеале я хотел бы, чтобы класс был похож на неизменяемые строки в python; вы можете использовать их, даже не замечая и даже не зная, что они неизменны, и они просто ведут себя так, как вы ожидаете; они просто работают.
РЕДАКТИРОВАТЬ
Конечно, я могу обойти это, передавая по значению или используя временную переменную (что я сейчас и делаю). Я спрашиваю о том, "как передавать ссылки по значению в C++"
Я ожидаю, что ответ будет вращаться вокруг чего-то в STL, сейчас я изучаю семейство шаблонов smart_ptr.
ОБНОВИТЬ
Спасибо за ответы, я понимаю, что нет указателей. (см. мой другой вопрос, который действительно является продолжением этого)
6 ответов
В Java и C# вы на самом деле не имеете дело со ссылками - они больше похожи на дескрипторы или указатели. Ссылка в C++ - это действительно другое имя для исходного объекта, а не указатель на него (хотя он может быть реализован с помощью указателя). Когда вы назначаете значение для ссылки, вы присваиваете сам объект. Существует путаница в том, что для инициализации ссылки вы можете использовать =
символ, но это инициализация, а не присвоение.
Imm im, im2, im3;
Imm &imr = im; // initialize a reference to im
imr = im2; // assign im2 to imr (changes the value of im as well)
Imm *imp = &im; // initialize a pointer to the address of im
imp = &im3; // assign the address of im3 to imp (im is unnaffected);
(*imp) = im2; // assign im2 to imp (modifies im3 as well).
Если вы специально хотите передать "ссылки по значению", то вы, по сути, просите о противоречии в терминах. Ссылки по определению передаются по ссылке. Как указано в другом месте, вы можете передать указатель по значению или же прямое значение. Если вы действительно хотите, вы можете удерживать ссылку в классе и передавать ее по значению:
struct ImmRef
{
Imm &Ref;
ImmRef(Imm &ref) : Ref(ref) {}
};
Также обратите внимание, что const, примененный к ссылке, делает константу указанного объекта постоянной, а не ссылку. Ссылки всегда постоянны.
Разве назначение по определению не является постоянной операцией?
Похоже, вы пытаетесь присвоить что-то константной ссылке, что полностью опровергает идею константной ссылки.
Я думаю, что вы можете искать указатель вместо ссылки.
Это не работает так в C++.
Когда вы передаете ссылку на объект, вы фактически передаете адрес в памяти объекта. Ссылки не могут быть повторно размещены на других объектах, следовательно, пословица C++ "ссылка - это объект". Вы должны сделать копию, чтобы изменить ее. Java сделает это за кулисами для вас. C++, вам просто нужно скопировать его.
Разве вы не забыли установить вызываемый метод как const?
РЕДАКТИРОВАТЬ: так, с фиксированной константой.
Может быть, вы должны сделать что-то вроде
Imm & tmp = p_im.someOtherOp();
Затем выполните дальнейшую операцию с переменной tmp.
Если вы установите переменную или параметр как const &, вы просто не сможете назначить их.
В C++ есть нечто лучшее, чем неизменяемые типы -const
. Один тип может быть изменяемым или нет, в зависимости от ваших потребностей. Тем не менее, есть полезные шаблоны для работы с копиями с коротким сроком службы (почти):
void f(const X &x) {
// Trivial case: unconditional copy
X x2=transform(x);
// Less trivial: conditional copy
std::optional<X> maybe;
const X &use=need_copy ? maybe.emplace(transform(x)) : x;
use.go(); // whichever, x or *maybe
} // *maybe destroyed iff created, then x2 destroyed
std::unique_ptr
может использоваться аналогичным образом до C++17, хотя функция может тогда, конечно, бросать std::bad_alloc
.
Отметьте это, чтобы узнать о временном сроке жизни http://herbsutter.wordpress.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
Imm Imm::someOp( const Imm& p_im ) const
{
...
//Imm& im = p_im.someOtherOp(); // will *not* work
const Imm& im = p_im.someOtherOp(); // will work, but you get a const reference
...
}
Но вы можете использовать boost::shared_ptr
shared_ptr<Imm> Imm::someOtherOp() const
{
shared_ptr<Imm> ret = new Imm;
...
return ret;
}
shared_ptr<Imm> Imm::someOp(const share_ptr<Imm>& p_im) const
{
shared_ptr<Imm> im = p_im->someOtherOp();
}
Вам нужно сделать новую копию вашего входящего аргумента. Вы можете делать то, что хотите, несколькими эквивалентными способами: 1) вы можете передавать по значению:
Imm Imm::someOp( Imm im ) const {
im = im.someOtherOp(); // local im is a copy, original im not modified
return im; // return by value (another copy)
}
или 2) вы можете передать по ссылке и сделать копию явно:
Imm Imm::someOp( const Imm & im ) const {
Imm tmp = im.someOtherOp(); // local tmp is a copy
return tmp; // return by value (another copy)
}
обе формы эквивалентны.