可以`* this`是`move()`d? [英] Can `*this` be `move()`d?

查看:156
本文介绍了可以`* this`是`move()`d?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想定义一个类来编组数据;当编组完成后,我想将移动中的编组数据输出,这可能会使编组对象失效。



我相信这可以用 static 函数 extractData

  class Marshaller 
{
public:
static DataType extractData(Marshaller& marshaller)
{
return std :: move(marshaller.data);
}
private:
DataType data;
}

这有点不方便调用:

  Marshaller marshaller; 
// ...做一些编组...
DataType marshalled_data {Marshaller :: extractData(std :: move(marshaller))};

那么我可以用一个成员函数来包装它吗?

  DataType Marshaller :: toDataType()
{
return Marshaller :: extractData(std :: move(* this));
}

这当然可以使用:

  DataType marshalled_data {marshaller.toDataType()}; 

...对我来说,看起来好多了。但是 std :: move(* this)事情看起来非常可疑。在调用 toDataType()的上下文中, marshaller 不能再次使用,认为编译器可以知道:函数的主体可以在调用者的编译单元之外,因此没有什么可以指示 marshaller 已经具有 move )



这是未定义的行为吗?是否完全正常?或者介于两者之间?是否有更好的方法来完成同样的目标,最好不使用宏或要求调用者显式地 move marshaller

编辑:使用G ++和Clang ++,我发现我不仅可以编译上面的用例,但我可以实际

>继续通过编组器对底层数据进行修改,然后使用 toDataType 函数重新提取修改的数据。我还发现 marshalled_data 中的已提取的数据仍然被 marshaller ,这表明 marshalled_data marshaller 和调用上下文之间共享,因此我怀疑有内存泄漏或未定义的行为(双重删除)。



编辑2:如果我在 DataType 的析构函数,当调用者离开作用域时,它会出现两次。如果我在其中包含数组的 DataType 中包含数据成员,以及相应的 new [] delete [] ,我得到一个 glibc 双自由或腐败的错误。因此,我不确定这个是否可以安全,即使几个答案都说它在技术上允许。一个完整的答案应该解释什么是正确使用这种技术需要一个非平凡的 DataType 类。



strong> EDIT 3:这是一个兔子洞/蠕虫病毒,我已经打开了另一个问题以解决我的剩余问题。

解决方案

调用 move (* this) move 本质上只是对被调用的函数的一个提示,它可能会窃取对象的内部。在类型系统中,这个承诺通过&& 引用表示。



以任何方式。 move 不会执行任何类型的破坏 - 如上所述,它只是让我们调用&& 参数。接收移动对象(在这种情况下 extractData )的函数也不会做任何破坏。事实上,它需要将对象留在有效但未指定的状态。基本上,这意味着它必须能够以正常方式(通过 delete 或通过超出范围,根据它的创建方式)销毁对象。 p>

所以,如果你的 extractData 做它应该和应该离开的对象处于允许它被破坏的状态,对于编译器没有任何未定义的或危险的。当然,代码的用户可能会遇到一个问题,因为它不是完全明显的,该对象正在移动(并且可能不会包含任何数据)。这可能通过更改函数名称可能更清楚一点。或&&& (作为另一个回答建议) - 规定整个方法。


I would like to define a class for marshalling data; when marshalling is finished, I would like to move the marshalled data out from within it, which will probably invalidate the marshalling object.

I believe this is possible with the static function extractData below:

class Marshaller
{
  public:
    static DataType extractData(Marshaller&& marshaller)
    {
      return std::move(marshaller.data);
    }
  private:
    DataType data;
}

This is a bit inconvenient to call, though:

Marshaller marshaller;
// ... do some marshalling...
DataType marshalled_data{Marshaller::extractData(std::move(marshaller))};

So can I wrap it with a member function?

DataType Marshaller::toDataType()
{
  return Marshaller::extractData(std::move(*this));
}

This would, of course, be called using:

DataType marshalled_data{marshaller.toDataType()};

...which, to me, looks much nicer. But that std::move(*this) thing looks awfully suspicious. In the context of the call to toDataType(), marshaller can't be used again, but I don't think the compiler can know that: the body of the function could be outside the caller's compilation unit, so there's nothing to indicate that marshaller has had move() applied to it.

Is this undefined behavior? Is it perfectly fine? Or somewhere in between? Is there a nicer way to accomplish the same goal, preferably without using a macro or requiring the caller to explicitly move marshaller?

EDIT: With both G++ and Clang++, I found that not only could I compile the above use case, but I could actually continue to make modifications to the underlying data via the marshaller, then re-extract the modified data using the toDataType function. I also found that the already-extracted data in marshalled_data continued to be changed by marshaller, which indicates that the marshalled_data is shared between the marshaller and the calling context, so I suspect that there is either a memory-leak or undefined behavior (from double-deletion) here.

EDIT 2: If I put a print statement in DataType's destructor, it appears twice when the caller leaves scope. If I include a data member in DataType that has an array in it, with a corresponding new[] and delete[], I get a glibc "double free or corruption" error. So I'm not sure how this could be safe, even though several answers have said that it's technically allowed. A complete answer should explain what is required to use this technique correctly with a non-trivial DataType class.

EDIT 3: This is enough of a rabbit-hole/can-of-worms that I've opened up another question to address my remaining concerns.

解决方案

There is nothing inherently unsafe about calling move(*this). The move is essentially just a hint to a function being called that it may steal the internals of the object. In the type system, this promise is expressed through && references.

This is not related to destruction in any way. The move does not perform any type of destruction - as mentioned, it just enables us to call functions taking && parameters. The function receiving the moved object (extractData in this case) also does not do any destruction. In fact, it needs to leave the object in a "valid but unspecified state". Essentially, this means that it must be possible to destroy the object in the normal way (by delete or by going out of scope, depending on how it was created).

So - provided your extractData does what it should and leaves the object in a state that allows it to be destructed later - there is nothing undefined or dangerous going on with respect to the compiler. There could of course be a problem with users of the code being confused, since it is not entirely obvious that the object is being moved from (and likely won't contain any data later). This could perhaps be made a little clearer by changing the function name. Or (as another answer suggested) by &&-qualifying the entire method.

这篇关于可以`* this`是`move()`d?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆