在 C++ 上覆盖运算符时促进 MOVE 操作的正确方法
Proper way to facilitate MOVE operation when overriding operator on C++
我不太熟悉移动在 C++ 中的工作原理,我需要一些帮助来澄清我的理解。我想重载 operator+,对此我有几个问题。
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n};
return tmp += m;
}
我的第一个问题是如何使临时对象可移动。如我上面的函数所示,两个参数都不会被改变,因此我需要创建第三个对象来执行操作。
如何使我的 return 值可用于移动操作。 return 值应该作为 ap_n&
的参考吗? return对象应该被std::move(tmp)
封装吗?还是就这样好了?
C++ 如何确定对象何时为右值,或确定移动操作适合对象,以及我如何告诉程序对象可安全地用于移动操作。
ap_n operator+(const ap_n&, const ap_n&); // defined as in first question
ap_n operator+(ap_n&& n, const ap_n& m) { return n += m; }
ap_n operator+(const ap_n& n, ap_n&& m) { return m += n; }
ap_n operator+(ap_n&& n, ap_n&& m) { return n += m; }
我的第二个问题是是否有必要创建接受右值参数的函数变体。现在我有 4 个函数,如图所示,能够接受普通对象和右值对象。
有必要把所有可能的组合都写成这样吗?如果我删除除第一个功能之外的所有功能,程序是否仍然能够正确执行移动操作?
作为调试提示,可以帮助正确处理这些事情的是在移动构造函数中打印一条消息
ap_n(ap_n&& o): x_(std::move(o.x_)) { std::cerr << "Move constructed\n"; }
加上其他构造函数和析构函数中的类似消息。然后,您将清楚地了解实例创建和销毁的时间和方式。
How can I make my return value usable for move operation. Is the return value supposed to be a reference as ap_n&? Should the return object be encapsulated by std::move(tmp)? Or is it alright as is?
Return 值的结果。 (不要 return 对局部变量的引用,因为局部变量会立即超出范围,使引用无效。)您可能会发现这篇简短的文章很有用:Tip of the Week #77: Temporaries, Moves, and Copies. For more depth, check out cppreference on copy elision.
how to make temporary objects movable
通过定义 move constructor/assignment 到你的类型。
Is the return value supposed to be a reference as ap_n&?
不适用于 operator+
当您 return 新对象时。
operator +=
另一方面 returns 引用 lhs,所以 returns ap_n&
.
How can I make my return value usable for move operation. Should the return object be encapsulated by std::move(tmp)? Or is it alright as is?
来自return statement,
当直接 returning 局部变量时会自动移动。
所以return tmp;
就足够了。
return std::move(tmp);
阻止 NRVO
return tmp += m;
进行复制,因为您不会 return “直接” tmp
.
你应该做的:
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n};
tmp += m;
return tmp; // NRVO, or automatic move
}
return std::move(tmp += m);
将阻止 NRVO,并执行移动。
How does C++ decide when an object is rvalue,
大致上,
- 变量是左值,因为有名称。
- 函数 returning 左值引用 (
ap_n&
) returns 左值。
- 函数 returning 右值引用 (
ap_n&&
),或按值 (ap_n
) returns 右值。
or decide that a move operation is suitable on an object, and how can I tell the program that an object is safe to use for move operation.
过载分辨率select有效候选之间的最佳匹配。
因此需要函数取值或右值引用(或转发引用)。
My second question is
好像不是第二个 ;-)
whether it is necessary to create variants of function that accept rvalue arguments. Right now I have 4 functions, as shown, to be able to accept normal objects and rvalue objects.
Is writing all the combinations possible like this necessary?
在一般情况下,通过 const 引用或值获取单个函数就足够了,
除非你想要那个优化。所以主要用于库编写器或关键代码。
请注意,您的重载应该重写以有效地执行移动操作(以重用输入的临时参数):
ap_n operator+(ap_n&& n, const ap_n& m)
{
n += m;
return std::move(n); // C++11; C++14, C++17
return n; // C++20
}
或
ap_n&& operator+(ap_n&& n, const ap_n& m)
{
return std::move(n += m);
}
If I remove all but the first function, would the program still be able to perform move operation correctly?
有
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n}; // copy
tmp += m;
return tmp; // NRVO, or automatic move
}
你有 1 个副本,一个 NRVO/move 用于任何类型的参数。
有
ap_n&& operator+(ap_n&& n, const ap_n& m) {
return std::move(n += m);
}
你没有移动,但你应该注意引用的生命周期,因为
auto ok = ap_n() + ap_n(); // 1 extra move
auto&& dangling = ap_n() + ap_n(); // No move, but no lifetime extension...
有
ap_n operator+(ap_n&& n, const ap_n& m) {
n += m;
return std::move(n); // C++11, C++14, C++17 // move
return n; // C++20 // automatic move
}
你有 1 步,没有副本。
auto ok = ap_n() + ap_n(); // 1 extra move possibly elided pre-C++17, 0 extra moves since C++17
auto&& ok2 = ap_n() + ap_n(); // No moves, and lifetime extension...
因此,如果有额外的超载,您可能会用副本来移动。
参考,修改后的代码如下
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n};
tmp += n;
return tmp; // Allows copy, NRVO, or move
}
ap_n operator+(ap_n&& n, const ap_n& m) {
n += m;
return std::move(n); // Allows copy, or move
}
ap_n operator+(const ap_n& n, ap_n&& m) {
m += n;
return std::move(m); // Allows copy, or move
}
函数的数量也减少到 3 个,因为同时采用两个右值引用的函数将自动使用第二个函数。
如果我仍然误解这一点,请告诉我。
我不太熟悉移动在 C++ 中的工作原理,我需要一些帮助来澄清我的理解。我想重载 operator+,对此我有几个问题。
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n};
return tmp += m;
}
我的第一个问题是如何使临时对象可移动。如我上面的函数所示,两个参数都不会被改变,因此我需要创建第三个对象来执行操作。
如何使我的 return 值可用于移动操作。 return 值应该作为 ap_n&
的参考吗? return对象应该被std::move(tmp)
封装吗?还是就这样好了?
C++ 如何确定对象何时为右值,或确定移动操作适合对象,以及我如何告诉程序对象可安全地用于移动操作。
ap_n operator+(const ap_n&, const ap_n&); // defined as in first question
ap_n operator+(ap_n&& n, const ap_n& m) { return n += m; }
ap_n operator+(const ap_n& n, ap_n&& m) { return m += n; }
ap_n operator+(ap_n&& n, ap_n&& m) { return n += m; }
我的第二个问题是是否有必要创建接受右值参数的函数变体。现在我有 4 个函数,如图所示,能够接受普通对象和右值对象。
有必要把所有可能的组合都写成这样吗?如果我删除除第一个功能之外的所有功能,程序是否仍然能够正确执行移动操作?
作为调试提示,可以帮助正确处理这些事情的是在移动构造函数中打印一条消息
ap_n(ap_n&& o): x_(std::move(o.x_)) { std::cerr << "Move constructed\n"; }
加上其他构造函数和析构函数中的类似消息。然后,您将清楚地了解实例创建和销毁的时间和方式。
How can I make my return value usable for move operation. Is the return value supposed to be a reference as ap_n&? Should the return object be encapsulated by std::move(tmp)? Or is it alright as is?
Return 值的结果。 (不要 return 对局部变量的引用,因为局部变量会立即超出范围,使引用无效。)您可能会发现这篇简短的文章很有用:Tip of the Week #77: Temporaries, Moves, and Copies. For more depth, check out cppreference on copy elision.
how to make temporary objects movable
通过定义 move constructor/assignment 到你的类型。
Is the return value supposed to be a reference as ap_n&?
不适用于 operator+
当您 return 新对象时。
operator +=
另一方面 returns 引用 lhs,所以 returns ap_n&
.
How can I make my return value usable for move operation. Should the return object be encapsulated by std::move(tmp)? Or is it alright as is?
来自return statement, 当直接 returning 局部变量时会自动移动。
所以return tmp;
就足够了。
return std::move(tmp);
阻止 NRVO
return tmp += m;
进行复制,因为您不会 return “直接” tmp
.
你应该做的:
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n};
tmp += m;
return tmp; // NRVO, or automatic move
}
return std::move(tmp += m);
将阻止 NRVO,并执行移动。
How does C++ decide when an object is rvalue,
大致上,
- 变量是左值,因为有名称。
- 函数 returning 左值引用 (
ap_n&
) returns 左值。 - 函数 returning 右值引用 (
ap_n&&
),或按值 (ap_n
) returns 右值。
or decide that a move operation is suitable on an object, and how can I tell the program that an object is safe to use for move operation.
过载分辨率select有效候选之间的最佳匹配。
因此需要函数取值或右值引用(或转发引用)。
My second question is
好像不是第二个 ;-)
whether it is necessary to create variants of function that accept rvalue arguments. Right now I have 4 functions, as shown, to be able to accept normal objects and rvalue objects.
Is writing all the combinations possible like this necessary?
在一般情况下,通过 const 引用或值获取单个函数就足够了, 除非你想要那个优化。所以主要用于库编写器或关键代码。
请注意,您的重载应该重写以有效地执行移动操作(以重用输入的临时参数):
ap_n operator+(ap_n&& n, const ap_n& m)
{
n += m;
return std::move(n); // C++11; C++14, C++17
return n; // C++20
}
或
ap_n&& operator+(ap_n&& n, const ap_n& m)
{
return std::move(n += m);
}
If I remove all but the first function, would the program still be able to perform move operation correctly?
有
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n}; // copy
tmp += m;
return tmp; // NRVO, or automatic move
}
你有 1 个副本,一个 NRVO/move 用于任何类型的参数。
有
ap_n&& operator+(ap_n&& n, const ap_n& m) {
return std::move(n += m);
}
你没有移动,但你应该注意引用的生命周期,因为
auto ok = ap_n() + ap_n(); // 1 extra move
auto&& dangling = ap_n() + ap_n(); // No move, but no lifetime extension...
有
ap_n operator+(ap_n&& n, const ap_n& m) {
n += m;
return std::move(n); // C++11, C++14, C++17 // move
return n; // C++20 // automatic move
}
你有 1 步,没有副本。
auto ok = ap_n() + ap_n(); // 1 extra move possibly elided pre-C++17, 0 extra moves since C++17
auto&& ok2 = ap_n() + ap_n(); // No moves, and lifetime extension...
因此,如果有额外的超载,您可能会用副本来移动。
参考
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n};
tmp += n;
return tmp; // Allows copy, NRVO, or move
}
ap_n operator+(ap_n&& n, const ap_n& m) {
n += m;
return std::move(n); // Allows copy, or move
}
ap_n operator+(const ap_n& n, ap_n&& m) {
m += n;
return std::move(m); // Allows copy, or move
}
函数的数量也减少到 3 个,因为同时采用两个右值引用的函数将自动使用第二个函数。
如果我仍然误解这一点,请告诉我。