2017-02-16 47 views
-1

Я создаю фреймворк, который будет читать XML-файл Tiled, и результирующий объект (tpp :: File) будет чистым неизменным (без сеттеров или конструкторов копирования/операторов присваивания). В принципе, он использует идею builder pattern, но вместо того, чтобы иметь 2 объекта с одинаковыми атрибутами, у меня будет один с основными атрибутами, а другой - «обернуть» его.Неизменяемые классы и конструкторы копирования

// Represents a Tiled's TMX file. This object is immutable. 
class TILEDPP_API File final 
{ 
public: 
    File() = default; 
    File(tpp::File&&) = default; 
    File(const tpp::File&) = delete; 
    File(const tpp::Path& path, tpp::FileMetadata& metadata); 

    File& operator = (tpp::File&&) = default; 
    File& operator = (const tpp::File&) = delete; 

    const tpp::Path& getPath() const; 
    const tpp::Header& getHeader() const; 
    const tpp::Layers& getLayers() const; 
    const tpp::TileSets& getTileSets() const; 

private: 
    const tpp::Path m_path; 
    tpp::FileMetadata m_metadata; // Should be const! 
}; 

// Represents the content of a Tiled's TMX file (header, sets, layers etc). 
// This struct is non-copyable due to its HUGE size. 
struct TILEDPP_API FileMetadata final 
{ 
    FileMetadata() = default; 
    FileMetadata(tpp::FileMetadata&&) = default; 
    FileMetadata(const tpp::FileMetadata&) = delete; 

    FileMetadata& operator = (FileMetadata&&) = default; 
    FileMetadata& operator = (const FileMetadata&) = delete; 

    tpp::Header header; 
    tpp::Layers layers; 
    tpp::TileSets sets; 
}; 

Затем, где-то в процессе создания файла, мы будем иметь это:

tpp::File FileReader::read(const std::string& path) 
{ 
    tpp::FileMetadata metadata = m_parser.parseMetadata(path); 

    return tpp::File(path, metadata); 
} 

выше фрагмент кода будет использовать File(const tpp::Path& path, tpp::FileMetadata& metadata) конструктор, как и ожидалось. Однако, если мы создадим tpp :: File tpp :: FileMetadata, он попытается использовать вместо этого конструктор File(const tpp::File&), который является удален. Почему это происходит?

Для справки, проект можно найти here. Любые мысли также очень ценятся.

ответ

0

он будет пытаться использовать File(const tpp::File&) конструктор вместо

Вместо неправильное слово.

Также является правильным словом.

До C++ 17 вы создаете временный File и возвращаете копию, созданную движением, из этой функции. (Эта конструкция перемещения может быть отменена, а в C++ 17 больше нет скопированной копии).

FileMetaData не поддерживает движение от FileMetadata const&&). Так что ваш =default в File(File&&) не работает; реализация по умолчанию не может работать.


Объекты не могут быть неизменными и практически подвижными.

Неизменяемые объекты не могут быть практически перемещены из C++; в то время как они являются неизменными во время разрушения, переезд из перед разрушением, когда они должны быть неизменными. Таким образом, вы не можете «вырвать свое состояние» и переместить его в другое место.

Я заявляю об этом, потому что, по всей видимости хотят, чтобы поддержать движение-с:

File(tpp::File&&) = default; 
File& operator = (tpp::File&&) = default; 

Если вы хотите перейти-из, Fileне может быть неизменны. Если вы хотите неизменно, то File не может быть перемещен.

Итак, предположим, что вы хотите сохранить неизменность.


=delete сделать те, а затем изменить

return tpp::File(path, metadata) 

к

return {std::move(path), std::move(metadata)}; 

и изменить

File(const tpp::Path& path, tpp::FileMetadata& metadata); 

в

File(tpp::Path&& path, tpp::FileMetadata&& metadata); 

и ваш код компилируется.

Чтобы позвонить по номеру read, сохраните возвращаемое значение в File&& или auto&& - ссылку rvalue на временный возврат read.

Примечание. Вы не можете сохранить это возвращенное File в виде более длинного срока службы, поскольку для этого требуется перемещение с File, которое неизменяемые объекты не позволяют.


В качестве альтернативы, отключение от неизменности. Сделать его неизменным исключая перемещение.

Объекты, которые являются неизменяемыми за исключением перемещения не сохраняют свои данные как const, потому что они мутируют его при перемещении. Каждый другой метод - const.

+0

Hi, @Yakk! Спасибо за ваш ответ! Не могли бы вы рассказать мне об этом: 'FileMetaData не поддерживает переход из FileMetadata const &&). Таким образом, ваш = default в файле (файл &&) не работает; никакая реализация по умолчанию не может работать. Кроме того, как «добавление поддержки» для перехода из этого требует, чтобы он * удалял * оператор перемещения/операнда перемещения ?! –

+0

@YvesHenri Где вы видите "добавление поддержки"? Кого вы цитируете? Ваш дизайн не может поддерживать перемещение; вы хотите неизменности, вы не можете поддерживать перемещение. Первоначальные комментарии о поддержке перемещения - это вывод you * want * move support; Я просто заявляю, что вы не можете этого сделать. '= Default' фактически не выполнял их; поэтому я сделал это явным, что это '= delete'd. Затем я изменил оператор return, чтобы построить вместо него место вместо возвращаемого значения. Это имеет серьезные ограничения, как я описываю до последней строки. После последней строки я говорю «хорошо, вот еще одно решение». – Yakk