关于后置构造函数和移动语义的问题

14

我之前已经问过类似的问题,但是对于某些细节仍不清楚。

  1. 在什么情况下会调用postblit构造函数?

  2. 移动一个对象的语义是什么?它会被postblitted和/或析构吗?

  3. 如果我通过值返回一个局部变量,会发生什么?它会被隐式移动吗?

  4. 如何将表达式强制转换为rvalue?例如,泛型swap会是什么样子?


一个通用的交换函数:void swap(T)(ref T t1, ref T t2) {T tmp = t1; t1 = t2; t2 = tmp;} - ratchet freak
@ratched:你有三个复制操作。我想要移动操作。 - fredoverflow
然后您需要转换为字节数组,请查看https://github.com/D-Programming-Language/phobos/blob/master/std/algorithm.d#L1483以获取phobos实现。 - ratchet freak
@ratchet freak:您可以在评论中像普通帖子一样使用Markdown来添加链接,即[string](url)。详情请参见此处 - Alexander Malakhov
@AlexanderMalakhov 我自七月份发布那篇文章以来就已经弄清楚了。 - ratchet freak
1
@ratchet freak:没有冒犯的意思,我发帖只是为了以防万一。 - Alexander Malakhov
2个回答

16
  1. 一个postblit构造函数在结构体被复制时调用 - 例如将结构体传递给一个函数。

  2. 移动是一种按位复制。postblit构造函数永远不会被调用。析构函数也永远不会被调用。比特位只是被复制了。原始数据被“移动”了,因此不需要创建或销毁任何内容。

  3. 它将被移动。这是移动的典型示例。

  4. 如果要使swap函数尽可能高效,它必须考虑到许多不同的情况。我建议直接使用std.algorithm中的swap函数。经典交换会导致复制,因此会调用postblit构造函数和析构函数。移动通常由编译器完成,而不是程序员。但是,查看官方实现的swap函数,看起来它会进行一些技巧以从中获得移动语义。无论如何,移动通常由编译器执行。它们是一种优化,只有在它知道可以这样做时才会执行(RVO是它可以执行的典型情况)。

根据TDPL(第251页),只有两种情况下,D 保证 进行移动操作:
  • 所有匿名 rvalue 都会被移动,而不是复制。当源是匿名 rvalue 时(即在函数 hun 中出现的临时变量),不会插入对 this(this) 的调用。
  • 在函数内部分配到堆栈中并返回的所有命名临时对象都会省略调用 this(this)
  • 不能保证观察到其他潜在的省略。
因此,编译器可能会在其他地方使用移动操作,但不能保证会这样做。

std.algorithm的链接对我来说已经失效了。 - BCS
我不知道为什么。它在我的电脑上运行得很好。它只是指向github上的std.algorithm链接。 - Jonathan M Davis
@BCS:啊,所以你需要更深入地了解。 - user541686
我进行了调整,使得第一个链接现在指向Phobos文档中的swap,稍后再链接到实际实现,因此如果有人在使用github链接时遇到任何问题,现在将是“查看官方实现”的链接。但我没有遇到过链接问题。也许这是浏览器问题。 - Jonathan M Davis

3

就我所了解的情况:

1)当一个结构体被复制时,与移动或构造不同。

2)移动语义的重点是两者都不需要发生。结构体的新位置使用结构体的按位复制进行初始化,旧位置超出范围并且无法访问。因此,结构体已从A移动到B。

3)这是典型的移动情况:

S init(bool someFlag)
{
    S s;
    s.foo = someFlag? bar : baz;
    return s; // `s` can now be safely moved from here...
}

// call-site:
S s = init(flag);
//^ ... to here.

只是为了澄清一下,当“旧位置超出范围”时,它不会被销毁,对吗? - fredoverflow
确切地说 - 它既不是在调用现场构建的,也不是在后处理中进行位块传输的,它只是存在着。 - jA_cOp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接