Я предполагаю, что ваша цель - истинная неизменность - каждый объект, когда он построен, не может быть изменен. Нельзя ставить один объект поверх другого.
Самым большим недостатком вашего дизайна является то, что он несовместим с семантикой перемещения, что может сделать функции, возвращающие такие объекты, более практичными.
Например:
struct RockSolidLayers {
const std::vector<RockSolid> layers;
};
мы можем создать один из них, но если у нас есть функция для его создания:
RockSolidLayers make_layers();
он должен (логически) скопировать свое содержимое в возвращаемое значение или использовать синтаксис return {}
для его непосредственного построения. Снаружи нужно либо сделать:
RockSolidLayers&& layers = make_layers();
или снова (логически) копировать-конструкцию. Невозможность переместить-конструкцию помешает ряду простых способов получить оптимальный код.
Теперь обе эти копии-конструкции опущены, но сохраняется более общий случай - вы не можете перемещать свои данные из одного именованного объекта в другой, поскольку C ++ не имеет операции удаления и перемещения, которая одновременно берет переменную за пределы области видимости и использует ее для построения чего-то еще.
И случаи, когда C ++ неявно перемещает ваш объект (например, return local_variable;
) до уничтожения, блокируются вашими const
членами данных.
В языке, разработанном для неизменяемых данных, он будет знать, что может перемещать ваши данные, несмотря на его (логическую) неизменяемость.
Один из способов решить эту проблему - использовать кучу и хранить данные в std::shared_ptr<const Foo>
. Теперь const
ness находится не в данных элемента, а в переменной. Вы также можете предоставлять только фабричные функции для каждого из ваших типов, которые возвращают указанное выше shared_ptr<const Foo>
, блокируя другие конструкции.
Такие объекты могут быть составлены с Bar
хранением std::shared_ptr<const Foo>
членов.
Функция, возвращающая std::shared_ptr<const X>
, может эффективно перемещать данные, а состояние локальной переменной может быть перемещено в другую функцию после того, как вы закончите с ней, без возможности возиться с реальными данными.
Что касается родственной техники, то в менее ограниченном C ++ является идеальным брать такие shared_ptr<const X>
и хранить их в типе оболочки, который делает вид, что они не являются неизменяемыми. Когда вы выполняете операцию изменения, shared_ptr<const X>
клонируется и модифицируется, а затем сохраняется. Оптимизация знает, что shared_ptr<const X>
на самом деле shared_ptr<X>
(примечание: убедитесь, что фабричные функции возвращают shared_ptr<X>
приведение к shared_ptr<const X>
, или это на самом деле неверно), и когда use_count()
равно 1, вместо этого отбрасывает const
и изменяет его напрямую. Это реализация метода, известного как копирование при записи.
Теперь, когда С ++ развивается, появляется больше возможностей для исключения. Даже C ++ 23 будет иметь более продвинутые возможности. Elision - это когда данные не перемещаются и не копируются логически, а имеют два разных имени: одно внутри функции, а другое снаружи.
Надеяться на это по-прежнему неудобно.
person
Yakk - Adam Nevraumont
schedule
24.11.2014
const
члены данных имеют то преимущество, что вы получите ошибку, если забудете инициализировать такой член базового типа. Недостатком является то, что вы не можете присвоить переменным типа. - person Cheers and hth. - Alf   schedule 11.11.2014const
, поэтому я бы предпочел сделать их частными и вместо этого предоставлять толькоconst
геттеры и методы, как предлагает oikosdev. - person Joe   schedule 22.11.2014