有什么方法可以正确 std::move const 数据吗?

Is there any way to correctly std::move const data?

我的问题是我必须在复制 const 数据和移动非 const 数据之间做出选择,而且我在概念上看不出为什么在我的情况下将 const 数据移动到一个常量目的地。我也知道从 const 对象移动是不合逻辑的 - 所以也许有其他方法可以实现我正在尝试做的事情...

我有一个生成器 class,它设置了一个我不想复制的 BigData 对象。一旦构建器完成,它将 'emits' 数据作为常量,因为在那之后不应修改它。构建器是临时的,然后可以丢弃,但数据应该继续存在,作为包装器 class 中的 const 成员可以查询它。构建器是一个通用类型(不知道数据的细节),而包装器是特定于数据内容的。这是通过数据中的类型擦除实现的,但这与这个问题无关,只是要明确表示不希望合并构建器和包装器。

下面的代码描述了上面的内容,除非您删除 BigData 的移动构造函数,否则不会编译。编译错误表明这段代码会复制数据,这是不可取的:

prog.cpp:26:57: error: use of deleted function ‘constexpr BigData::BigData(const BigData&)’
     Wrapper( const BigData&& In ) : Data( std::move(In) ) {}
                                                         ^
class BigData
{
    // Only a builder can create data
    friend class Builder;
    BigData() = default;
public:
    // Move only
    BigData( BigData&& ) = default;
};

class Builder
{
    BigData Data;
public:
    const BigData&& Build() & { return std::move(Data); }
    // Functions to set up data go here
};

class Wrapper
{
    const BigData Data;
public:
    Wrapper( const BigData&& In ) : Data( std::move(In) ) {}
    
    // Functions to query data go here
};

std::unique_ptr<Wrapper> DoBuild()
{
    Builder b;
    // call functions on b to set up data here
    return std::make_unique<Wrapper>( b.Build() );
}

以上代码也可以在https://ideone.com/tFkVEd.

找到

问题可以通过将所有 const BigData&& 更改为仅 BigData&& 来解决,但我不希望数据在从 Build() 检索到后可以更改。

最后,这个问题也可以通过在 unique_ptr<const BigData> 中从 Build 返回数据来解决,并让 Wrapper 拥有它的所有权,但我的问题是指针可以为空,所以在 Wrapper 中使用 Data 必须首先检查 Data 指针是否有效。这就是为什么我想通过右值引用将 const 数据移动到包装器中,也是为什么必须用数据构造包装器的原因。

有没有办法实现我想要的,或者我是否遇到了 c++ 的限制?

非常感谢您的投入。

注意:我在尝试自己解决这个问题时遇到的相关问题:

无法 std::move a const T 因为它会改变给定的对象。 (除非你让成员 mutable 并让 T(const T&&) 构造函数来做移动,我不推荐这样做)。

我认为更好的解决方案是将所有变异函数设为私有,这样只有构建者才能访问它们,并且 return T&&.

首先,移动一个对象,确切的意思是修改它。

其次,我认为您不应该阻止对 BigData 的所有修改。试图 return 一个 BigData const&& 并阻止它被修改,就像试图阻止它被 const_cast 编辑一样,这是一种浪费。在这种情况下,您可以声明 Builder 中修改 BigData 绝对是 未定义的行为 ,除了移动它 然后 return 一个 BigData&&.

第三,如果你希望它不被完全修改(移动除外),你应该考虑是否构造一个Wrapper是唯一的用法Builder。如果是这样,为什么不提供构造函数 Wrapper(Build&) 来做到这一点?它可以防止从 BuilderWrapper.

的所有潜在修改

最后,为了回答如何移动一个const BigData对象这个问题,我建议BigData(BigData const&&)const_cast,这意味着你允许所有 const BigData 都将被移动,就好像它是非常量一样。