不可变类和复制构造函数

时间:2017-02-16 20:14:08

标签: c++11 immutability copy-constructor

我正在创建一个框架,它将读取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。任何想法也非常感谢。

1 个答案:

答案 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

相关问题