如果std :: move将导致意外的复制,则强制发生编译时错误? [英] Force a compile time error if std::move will result in an unintended copy?
问题描述
斯科特·迈耶斯(Scott Meyers)在2013年GoingNative演讲中指出,std::move
不能保证所生成的代码会真正执行移动.
In his GoingNative 2013 talk, Scott Meyers pointed out that std::move
is no guarantee that the generated code will actually perform a 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
}
在这里,不能应用move构造函数,但是由于重载解析,因此将使用常规的复制构造函数.此后备选项对于与C ++ 98代码向后兼容可能至关重要,但是在上面的示例中,它很可能不是程序员想要的.
Here, the move constructor cannot be applied but because of overload resolution, the normal copy constructor will be used instead. This fallback option may be crucial for backward compatibility with C++98 code, but in the example above it is most likely not what the programmer intended.
是否有一种方法可以强制调用move构造函数?
例如,假设您要移动一个巨大的矩阵.如果您的应用程序确实依赖于要移动的Matrix,那么在无法移动的情况下立即获得编译错误将非常有用. (否则,性能问题可能会通过单元测试轻易滑落,并且只有在进行概要分析后才能发现.)
For example, assume that you want to move a huge matrix. If your application really depend on the Matrix to be moved, it would be great to immediately get a compile error if a move is not possible. (Otherwise, you the performance problem may slip easily through unit tests and you will only find out after some profiling.)
让我们将此保证移动称为strict_move
.我希望能够编写如下代码:
Lets call this guaranteed move strict_move
. I would like to be able to write code like this:
void bar(Matrix x, const Matrix y) {
Matrix x2 = strict_move(x); // OK
Matrix y2 = strict_move(y); // compile error
}
有可能吗?
感谢出色的答案!有一些合理的要求来澄清我的问题:
Thanks for the great answers! There were some legitimate requests to clarify my question:
- 如果输入为const,
strict_move
应该失败吗? - 如果结果不会导致实际的移动操作(即使副本可能与移动一样快,例如
const complex<double>
),strict_move
是否会失败? - 都是吗?
- Should
strict_move
fail if the input is const? - Should
strict_move
fail if the result will not lead to an actual move operation (even though the copy might be as fast as a move, e.g.,const complex<double>
)? - Both?
我最初的想法很模糊:我认为Scott Meyers的示例非常令人震惊,因此我想知道是否可以让编译器防止此类意外复制.
My original idea was very vague: I considered Scott Meyers examples quite alarming, so I wondered if it is possible to have the compiler prevent such unintended copies.
斯科特·迈耶斯(Scott Meyers)在讲话中提到,一般编译器警告不是一种选择,因为它会导致大量误报.相反,我想向编译器传达类似信息我100%确保必须始终导致移动操作,并且对于该特定类型而言,副本过于昂贵".
Scott Meyers mentioned in his talk that a general compiler warning is not an option as it would result in a huge number a false positives. Instead I want to communicate to the compiler something like "I'm 100% sure that this must always resulting in a move operation and a copy is too expensive for this specific type".
因此,我会毫不客气地说strict_move
在两种情况下都将失败.同时,我不确定什么是最好的.我没有考虑的另一个方面是noexcept
.
Thus, I would have offhandedly said that strict_move
should fail in both cases. Meanwhile I'm not sure what would be best. Another aspects that I didn't consider is noexcept
.
从我的角度来说,strict_move
的确切语义是开放的.可以防止在编译时出现一些愚蠢的错误而又没有严重缺陷的一切都很好.
From my side, the exact semantics of strict_move
are open. Everything that helps to prevent some dumb mistakes at compile time without having serious drawbacks is fine.
推荐答案
我建议不要编写用于检测const
的常规strict_move
.我认为这并不是您真正想要的.您是否要标记const complex<double>
或const pair<int, int>
?这些类型将以它们移动的速度复制.标记它们只是一个刺激.
I advise against writing a general strict_move
that is detecting const
. I think that is not really what you're looking for. Do you want this to flag a const complex<double>
, or a const pair<int, int>
? These types will copy as fast they move. Flagging them would just be an irritant.
如果要执行此操作,我建议改为检查类型是否为noexcept MoveConstructible
.这对于std::string
非常适用.如果string
的副本构造函数被意外调用,则它不是noexcept,因此将被标记.但是,如果偶然调用了pair<int, int>
的副本构造函数,您真的在乎吗?
If you want to do this, I recommend instead checking to see if the type is noexcept MoveConstructible
. This will work perfectly for std::string
. If the copy constructor of string
is accidentally called, it is not noexcept, and therefore will be flagged. But if the copy constructor of pair<int, int>
is accidentally called, do you really care?
这是它的示意图:
#include <utility>
#include <type_traits>
template <class T>
typename std::remove_reference<T>::type&&
noexcept_move(T&& t)
{
typedef typename std::remove_reference<T>::type Tr;
static_assert(std::is_nothrow_move_constructible<Tr>::value,
"noexcept_move requires T to be noexcept move constructible");
static_assert(std::is_nothrow_move_assignable<Tr>::value,
"noexcept_move requires T to be noexcept move assignable");
return std::move(t);
}
我还决定对is_nothrow_move_assignable
进行检查,因为您不知道客户端是在构造还是在分配lhs.
I decided to check against is_nothrow_move_assignable
as well, as you don't know whether the client is constructing or assigning the lhs.
我选择使用内部static_assert
而不是外部enable_if
,因为我不希望noexcept_move
过载,并且static_assert
触发时将产生更清晰的错误消息.
I opted for internal static_assert
instead of an external enable_if
because I don't expect noexcept_move
to be overloaded, and the static_assert
will yield a clearer error message when triggered.
这篇关于如果std :: move将导致意外的复制,则强制发生编译时错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!