【发布时间】:2013-09-09 21:51:05
【问题描述】:
在他的 GoingNative 2013 演讲中,Scott Meyers 指出 std::move 并不能保证生成的代码会实际执行移动。
例子:
void foo(std::string x, const std::string y) {
std::string x2 = std::move(x); // OK, will be moved
std::string y2 = std::move(y); // compiles, but will be copied
}
在这里,不能应用移动构造函数,但由于重载解析,将使用普通的复制构造函数。这个回退选项对于向后兼容 C++98 代码可能至关重要,但在上面的示例中,它很可能不是程序员的意图。
有没有办法强制调用移动构造函数?
例如,假设您要移动一个巨大的矩阵。如果您的应用程序确实依赖于要移动的矩阵,那么在无法移动的情况下立即获得编译错误会很棒。 (否则,性能问题可能很容易在单元测试中溜走,只有经过一些 profiling 才能发现。)
让我们将这个有保证的移动称为strict_move。我希望能够编写这样的代码:
void bar(Matrix x, const Matrix y) {
Matrix x2 = strict_move(x); // OK
Matrix y2 = strict_move(y); // compile error
}
有可能吗?
编辑:
感谢您的精彩回答!有一些合法的要求可以澄清我的问题:
- 如果输入为 const,
strict_move是否应该失败? - 如果结果不会导致实际的移动操作,
strict_move是否应该失败(即使复制可能与移动一样快,例如,const complex<double>)? - 两者都有?
我最初的想法非常模糊:我认为 Scott Meyers 的示例非常令人担忧,所以我想知道是否可以让编译器阻止此类意外复制。
Scott Meyers 在他的演讲中提到,一般编译器警告不是一种选择,因为它会导致大量误报。相反,我想与编译器进行交流,例如“我 100% 确定这必须始终导致移动操作,并且对于这种特定类型而言,副本过于昂贵”。
因此,我会随便说strict_move 在这两种情况下都应该失败。同时我不确定什么是最好的。另一个我没有考虑的方面是noexcept。
在我看来,strict_move 的确切语义是开放的。任何有助于在编译时防止一些愚蠢错误而又没有严重缺陷的东西都可以。
【问题讨论】:
-
制作您自己的
move版本来检查返回类型? -
等等,您是要检查输入是否不是 const,还是要检查实际移动是否会发生。因为第二个是个坏主意,下面的答案忽略了这种解释。
-
@MooingDuck 有趣,我没有详细考虑过这种差异,我只是想有一个防止无意行为的安全网。我同意阻止移动
const对象更安全,但是反对另外检查移动构造函数是否可用并且会被调用的论点是什么?我可以理解,在库代码中使用这样的构造可能会使代码不灵活,因为它排除了所有没有移动构造函数的类,并且可能会导致转换问题。但是,仅在您自己的代码库的性能关键部分使用它有什么缺点? -
@PhilippClaßen:因为它会导致代码不灵活,因为它会排除所有没有移动构造函数的类,并且可能会导致转换问题。
-
如果你关心的主要是复制一个巨大的矩阵(你的类矩阵持有)的问题,那么使用 strict_move 并不能完全解决你的问题。 (如果您尝试移动 std::vector
并且您的矩阵移动构造函数可能会引发异常,那么编译器实际上会复制向量元素)。更多详情见我的回答
标签: c++ c++11 move-semantics