Есть ли элегантный способ обмена ссылками на С++?
Иногда классы ссылаются на другие классы. Реализация std::swap()
для таких классов не может быть простой, поскольку это приведет к замене исходных экземпляров вместо ссылок. Приведенный ниже код иллюстрирует это поведение:
#include <iostream>
class A
{
int& r_;
public:
A(int& v) : r_(v) {}
void swap(A& a)
{
std::swap(r_, a.r_);
}
};
void test()
{
int x = 10;
int y = 20;
A a(x), b(y);
a.swap(b);
std::cout << "x=" << x << "\n"
<< "y=" << y << "\n";
}
int main()
{
test();
return 0;
}
Простой обходной путь с объединением:
class A
{
union
{
int& r_;
size_t t_;
};
public:
A(int& v) : r_(v) {}
void swap(A& a)
{
std::swap(t_, a.t_);
}
};
Это эффективный, но не красивый. Есть ли лучший способ заменить две ссылки на С++? Кроме того, как стандарт С++ объясняет смешение ссылок и значений в одном объединении, учитывая, что в книге Stroustrup "Язык программирования С++" "ссылка" определяется как "альтернативное имя объекта, псевдоним" (стр .189).
Ответы
Ответ 1
Объединенный трюк, который вы используете, примерно так же не переносится, как и код. Стандарт не устанавливает никаких требований относительно того, как компиляторы реализуют ссылки. Они могут (и, скорее всего, делать) использовать указатели под капотом, но это никогда не гарантируется. Не говоря уже о том, что sizeof(size_t)
и sizeof(T*)
не обязательно должны быть равными.
Лучший ответ на вашу проблему: не используйте ссылочные элементы, если вам нужен назначаемый класс /swappable. Вместо этого используйте вместо этого указатель. В конце концов, ссылки не подлежат переустановке по определению, но, желая, чтобы класс swappable, вы хотите что-то повторно. И это указатель.
Ответ 2
Вы можете использовать std::reference_wrapper
вместо прямой ссылки (которой вы не можете swap
) или указателя (который может быть nullptr
). Что-то вроде:
class A
{
std::reference_wrapper<int> r;
public:
A(int& v) : r(v) {}
void swap(A& rhs)
{
std::swap(r, rhs.r);
}
int& get() const { return r; }
};
Живой пример
Ответ 3
Ваша программа, использующая union
, не является законной, поскольку вы назначаете ссылку, но меняете целое число, а не намереваетесь снова использовать ссылку. Вы можете прочитать о том, почему здесь: Доступ к неактивному члену профсоюза и поведению undefined?
Простым решением здесь является использование указателей вместо ссылок. Тогда все будет работать гладко, без специального кода. Если вам это действительно не нравится, возможно, вы можете использовать boost::optional<int&>
вместо простых ссылок, но я бы изо всех сил пытался понять, как это будет лучше.
Ответ 4
По существу, что вы хотели бы сделать, если я понимаю ваш пример, это переподготовка ссылок. Этого нельзя сделать на С++. Вы можете либо обменять значения в упомянутых объектах, либо использовать указатели.