C++ такой веселый язык, что в нем, даже после многих многих лет написания кода, натыкаешься на какие-то, вроде как очевидные, вещи, о которых когда-то много раз читал в книгах, но которые приводят тебя в какое-то замешательство или ступор. Например, я писал на тему всем известного прикола прототип vs инстанцирование экземпляра класса, сегодня расскажу о том, как я недавно сильно возмущался по поводу того, что я не могу запихнуть в std::vector класс, у которого есть хоть одно поле данных с модификатором const.
На первый взгляд -- бредовое поведение.
Начинаем разбираться в сути проблемы -- для такого типа, по понятным причинам, нельзя сгенерировать оператор присваивания, т.к. в его теле вы не сможете поменять значение константного поля.
Зачем вектору оператор присваивания? Ну вот дергаете вы vector::erase -- посреди массива вам надо грохнуть элемент и сдвинуть в эту дырку стоящие от него справа экземпляры (отличная, кстати, идея -- двинуть все разом через memcpy). Контейнер это делает через последовательный вызов оператора присваивания, двигаясь слева направо.
Альтернативный вариант -- через размещающий delete вызвать деструктор для удаляемого элемента, а потом через размещающий new вызывать на этой ячейке массива конструктор копирования, передав ему аргументом стоящий справа элемент. Понятно, что в некоторых случаях, оператор присваивания может работать намного эффективнее, чем вызов деструктора + вызов конструктора копирования, например, если ваш тип содержит блок памяти фиксированного размера, выделенный из кучи. В этом случае оптимизированная версия оператора присваивания может вообще к куче не обращаться -- очевидный PROFIT перед тем, что будет делаться в деструкторе + конструкторе такого типа.
Потенциально, как я понимаю, std::vector может проверять наличие у типа оператора присваивания, и идти по одному или по другому пути, не вызывая ошибки компиляции.
Ваш Кэп.
На первый взгляд -- бредовое поведение.
Начинаем разбираться в сути проблемы -- для такого типа, по понятным причинам, нельзя сгенерировать оператор присваивания, т.к. в его теле вы не сможете поменять значение константного поля.
Зачем вектору оператор присваивания? Ну вот дергаете вы vector::erase -- посреди массива вам надо грохнуть элемент и сдвинуть в эту дырку стоящие от него справа экземпляры (отличная, кстати, идея -- двинуть все разом через memcpy). Контейнер это делает через последовательный вызов оператора присваивания, двигаясь слева направо.
Альтернативный вариант -- через размещающий delete вызвать деструктор для удаляемого элемента, а потом через размещающий new вызывать на этой ячейке массива конструктор копирования, передав ему аргументом стоящий справа элемент. Понятно, что в некоторых случаях, оператор присваивания может работать намного эффективнее, чем вызов деструктора + вызов конструктора копирования, например, если ваш тип содержит блок памяти фиксированного размера, выделенный из кучи. В этом случае оптимизированная версия оператора присваивания может вообще к куче не обращаться -- очевидный PROFIT перед тем, что будет делаться в деструкторе + конструкторе такого типа.
Потенциально, как я понимаю, std::vector может проверять наличие у типа оператора присваивания, и идти по одному или по другому пути, не вызывая ошибки компиляции.
Ваш Кэп.
No comments:
Post a Comment