如何检测移动预期案例中的意外副本
How to detect unexpected copy in move expected case
当我使用 lambda 表达式时,有时我会移动捕获可复制和可移动的对象。在我下面的示例中,对象是 t
,它的类型是 tracer
。在 lambda 表达式中,我想再次将 t
移动到其他函数 f()
。 f()
的参数是按值传递的,因为我想为f()
支持复制和移动。
main()
的第一部分我称为 f(std::move(t))
但它没有移动而是被复制,因为 t
被捕获为 const 变量。 main()
函数的第二部分,我将 mutable
添加到 lambda 表达式中。效果如我所料。
我经常忘记在 lambda 表达式中添加 mutable
。如果 t
是 move only 类型,第一种情况会导致编译错误。我很容易注意到这个问题。但是如果t
是可复制和可移动的,我就很难注意到意外的复制。我想检查那个案例。
有什么好的方法吗?
#include <iostream>
struct tracer {
tracer() {
std::cout << __PRETTY_FUNCTION__ << ":" << this << std::endl;
}
~tracer() {
std::cout << __PRETTY_FUNCTION__ << ":" << this << std::endl;
}
tracer(tracer const& other) {
std::cout << __PRETTY_FUNCTION__ << ":" << this << " <- " << &other << std::endl;
}
tracer(tracer&& other) {
std::cout << __PRETTY_FUNCTION__ << ":" << this << " <- " << &other << std::endl;
}
tracer& operator=(tracer const& other) {
std::cout << __PRETTY_FUNCTION__ << ":" << this << " <- " << &other << std::endl;
return *this;
}
tracer& operator=(tracer&& other) {
std::cout << __PRETTY_FUNCTION__ << ":" << this << " <- " << &other << std::endl;
return *this;
}
};
void f(tracer) {
}
int main() {
{
tracer t;
// move assign capture
[t = std::move(t)] { // forget write mutable
f(std::move(t)); // I expect move but copy due to lack of mutable
}();
}
std::cout << "---" << std::endl;
{
tracer t;
// move assign capture
[t = std::move(t)] () mutable {
f(std::move(t)); // Moved as I expected
}();
}
}
我想我想出了一个解决办法。我为 std::move()
.
编写了以下包装器
template <typename T>
typename std::remove_reference_t<T>&&
only_move(T&& t) {
static_assert(!std::is_const_v<std::remove_reference_t<T>>, "T is const. Fallback to copy.");
return std::move(t);
}
我用only_move()
替换了std::move()
如下:
int main() {
{
tracer t;
// move assign capture
[t = only_move(t)] { // got static assertion failed *1
f(only_move(t)); // I expect move but copy due to lack of mutable
}();
}
std::cout << "---" << std::endl;
{
tracer t;
// move assign capture
[t = only_move(t)] () mutable {
f(only_move(t)); // Moved as I expected
}();
}
}
只有当我忘记 mutable
时,我才会收到静态断言失败消息。
*1 错误:static_assert 由于要求 '!std::is_const_v' 而失败“T 是常量。后备复制。
当我使用 lambda 表达式时,有时我会移动捕获可复制和可移动的对象。在我下面的示例中,对象是 t
,它的类型是 tracer
。在 lambda 表达式中,我想再次将 t
移动到其他函数 f()
。 f()
的参数是按值传递的,因为我想为f()
支持复制和移动。
main()
的第一部分我称为 f(std::move(t))
但它没有移动而是被复制,因为 t
被捕获为 const 变量。 main()
函数的第二部分,我将 mutable
添加到 lambda 表达式中。效果如我所料。
我经常忘记在 lambda 表达式中添加 mutable
。如果 t
是 move only 类型,第一种情况会导致编译错误。我很容易注意到这个问题。但是如果t
是可复制和可移动的,我就很难注意到意外的复制。我想检查那个案例。
有什么好的方法吗?
#include <iostream>
struct tracer {
tracer() {
std::cout << __PRETTY_FUNCTION__ << ":" << this << std::endl;
}
~tracer() {
std::cout << __PRETTY_FUNCTION__ << ":" << this << std::endl;
}
tracer(tracer const& other) {
std::cout << __PRETTY_FUNCTION__ << ":" << this << " <- " << &other << std::endl;
}
tracer(tracer&& other) {
std::cout << __PRETTY_FUNCTION__ << ":" << this << " <- " << &other << std::endl;
}
tracer& operator=(tracer const& other) {
std::cout << __PRETTY_FUNCTION__ << ":" << this << " <- " << &other << std::endl;
return *this;
}
tracer& operator=(tracer&& other) {
std::cout << __PRETTY_FUNCTION__ << ":" << this << " <- " << &other << std::endl;
return *this;
}
};
void f(tracer) {
}
int main() {
{
tracer t;
// move assign capture
[t = std::move(t)] { // forget write mutable
f(std::move(t)); // I expect move but copy due to lack of mutable
}();
}
std::cout << "---" << std::endl;
{
tracer t;
// move assign capture
[t = std::move(t)] () mutable {
f(std::move(t)); // Moved as I expected
}();
}
}
我想我想出了一个解决办法。我为 std::move()
.
template <typename T>
typename std::remove_reference_t<T>&&
only_move(T&& t) {
static_assert(!std::is_const_v<std::remove_reference_t<T>>, "T is const. Fallback to copy.");
return std::move(t);
}
我用only_move()
替换了std::move()
如下:
int main() {
{
tracer t;
// move assign capture
[t = only_move(t)] { // got static assertion failed *1
f(only_move(t)); // I expect move but copy due to lack of mutable
}();
}
std::cout << "---" << std::endl;
{
tracer t;
// move assign capture
[t = only_move(t)] () mutable {
f(only_move(t)); // Moved as I expected
}();
}
}
只有当我忘记 mutable
时,我才会收到静态断言失败消息。
*1 错误:static_assert 由于要求 '!std::is_const_v' 而失败“T 是常量。后备复制。