То, что вы просите, не может быть сделано. Если у нас есть требование, чтобы структуры данных были размещены в стеке, то замена будет всегда приблизительно линейной по размеру структуры.
Если я объявлю Foo x
, где Foo
является классом или структурой со многими членами, тогда некоторое пространство в стеке должно быть выделено для хранения его содержимого. Теперь x
и любая ссылка (любого рода) на x
должны в конечном итоге указывать на это выделенное пространство стека. Следовательно, чтобы «поменять» x
с другим Foo
(скажем, y
), нам нужно преобразовать содержимое этого пространства так, чтобы оно выглядело как y
(а также преобразовать пространство, выделенное для y
, чтобы выглядетькак x
). Единственный способ сделать это - скопировать все данные из y
в пространство x
, операция, которая неизбежно становится более дорогой с увеличением размера Foo
. C ++ не позволяет вам «повторно указывать» переменные и ссылки - это именно то, для чего нужны указатели!
Итак, как мы можем обойти это?
Вы упомянули, что вы этого не делаетехотите, чтобы пользователи вашего класса занимались распределением кучи или «использовали указатели». Интересно, это именно потому, что вы хотите, чтобы память выделялась в стеке, или вы просто хотите, чтобы использование Foo
позволяло объявлять Foo x
внутри функций (т.е. не нужно заниматься каким-либо более сложным распределением)? ? Если это последнее, то вы можете просто превратить Foo
в обертку вокруг выделенной кучи внутренней структуры:
// Forward declaration. FooData will contain all of the many substructures and members
class FooData;
class Foo
{
FooData* data;
public:
Foo() : data(new FooData) {}
~Foo()
{
delete data;
}
void do_stuff()
{
std::cout << data->element_k;
// or whatever...
}
void swap(Foo &other)
{
// Now, no matter how big FooData is, we only ever need to swap one pointer
std::swap(data, other.data);
}
}
Итак, это делает перестановку (и вообще семантику перемещения) операцией постоянного времени,и мы все еще можем просто объявить Foo x
в нашем коде. Очевидным недостатком здесь является то, что каждая другая операция на Foo
будет включать (скрытое внутреннее) перенаправление указателя, поэтому является более дорогой. По всей вероятности, операции, отличные от подкачки, будут вызываться гораздо чаще, чем подкачка, так что вполне возможно, что это компромисс, который вы не хотите брать.
Реальный вопрос: почему это так? Вам важно, чтобы обмен был постоянным и прозрачным? Я бы сказал, что самым элегантным решением на сегодняшний день является простое документирование вашего класса Foo
, отметив, что он большой, и порекомендовав, чтобы в ситуациях, когда его много поменяли местами, он выделил кучу, и указатели поменялись местами. вместо. Вы можете сделать это более явным, если у нет метода обмена или дружественной функции на самом Foo
. Я понимаю, что это нарушает ваше предпочтение не требовать от пользователей явного распределения в куче, но в конечном итоге это наиболее гибкое решение: каждое использование класса может решить для себя, какой шаблон распределения лучше всего, учитывая его требования.