我正在创建一个框架,它将读取Tiled的XML文件,结果对象(tpp :: File)将是纯不可变的(没有setter或复制构造函数/赋值运算符)。基本上,它有点使用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为const,它将尝试使用File(const tpp::File&)
构造函数,而不是已删除。为什么会发生?!
作为参考,可以找到项目here。任何想法也非常感谢。
答案 0 :(得分: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&&
- 临时read
返回的右值引用中。
请注意,您无法将此返回的File
存储在更长寿命的变量中,因为这需要从File
移动,这是不可变对象不允许的。
或者,退出不变性。使其不可变除了移动。
不可变的除了移动之外的对象不会将其数据存储为const
,因为它们在移动时会变异。它们上的每个其他方法都是const
。