2017-02-16 28 views
-1

我正在制作一个框架,它将读取Tiled的XML文件,并且生成的对象(tpp :: File)将是纯不可变的(不包含setters或复制构造函数/赋值运算符)。基本上,它使用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

它会尝试使用File(const tpp::File&)构造函数,而不是

反而是错误的单词。

也是正确的词。

在C++ 17之前,您正在创建一个临时的File并从该函数返回一个移动构造的副本。 (这个移动的构造可能会被忽略,并且在C++ 17中没有任何构造复制的动作)。

FileMetaData不支持从FileMetadata const&&)移动。所以你的=defaultFile(File&&)不起作用;没有默认实现可以工作。


对象不能是不可变的,而且实际上是可移动的。

不可变对象不能实际移动 - 从C++中;虽然在销毁期间它们是不可改变的,但是在销毁前发生时,它们必须是不可变的。所以你不能“摧毁他们的状态”并把它移到其他地方。

我说明这一点,因为你似乎支持移动,从:如果你想移动,从,File不能是一成不变的

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

。如果你想不变,那么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

+0

嗨,@Yakk!感谢您的回复!您能否请赐教我:'FileMetaData不支持从FileMetadata中移出const &&)。所以你在File(File &&)中的默认值不起作用;没有默认的实现可以工作。另外,如何为移动从“添加支持”需要删除移动构造函数/分配操作符? –

+0

@YvesHenri你在哪里看到“添加支持”?你在引用谁?您的设计无法支持移动;你想要不变性,你不能有移动支持。关于移动支持的最初评论是我推断你*想*移动支持;我只是说你不能拥有它。 '=默认'实际上没有实现它们;所以我明确表示它是'= delete'd。然后,我改变了return语句来构造就地而不是移入返回值。这有严重的局限性,正如我在最后一行之前所描述的那样。在最后一行之后,我说“好吧,这是另一种解决方案”。 – Yakk