什么时候在 C++ 中提供自定义交换功能?
When to provide custom swap function in C++?
考虑使用移动构造函数 (MC)、移动赋值运算符 (MAO) 和自定义交换来保存一些资源(为简单起见,此处用整数成员变量表示)的自定义 class X
功能:
class X {
int i_;
public:
X(int i) : i_(i) { }
X(X&& rhs) = default; // move constructor
X& operator=(X&& rhs) = default; // move assignment operator
// X& operator=(X rhs) { std::swap(i_, rhs.i_); return *this; } // unifying assign. op.
friend void swap(X& lhs, X& rhs) { std::swap(lhs.i_, rhs.i_); }
int i() const { return i_; }
};
在以下基准代码中:
int main() {
int i1, i2;
std::cin >> i1 >> i2;
X x1(i1), x2(i2);
std::cout << x1.i() << x2.i();
swap(x1, x2);
std::cout << x1.i() << x2.i();
std::swap(x1, x2);
std::cout << x1.i() << x2.i();
}
swap(x1, x2)
只是 交换 2 个整数 。
std::swap(x1, x2)
调用 1x MC 和 2x MAO.
- 如果 MAO 被统一赋值运算符 (UAO) 替代,
std::swap(x1, x2)
调用 3x MC 和 2x UAO。
(使用 gcc/libstdc++ 测试)。
看来,自定义swap
非常有价值,因为它避免了很多函数调用。但是,当我查看此程序的反汇编输出时:https://godbolt.org/g/RMfZgL,MC、MAO、UAO、swap
和 std::swap
的所有功能都 直接内联到 main
(gcc/clang 带有 -O2
标志)。此外,程序中没有创建 X
类型的对象,它仅适用于机器代码级别的整数。
因此,在我看来,没有理由为像 X
这样的 class 提供自定义 swap
函数。
问题:
- 假设编译器将优化 MC 和
std::swap
内部的 MAO/UAO 调用是一种好习惯吗?或者,我应该为所有自定义 class 定义自定义 swap
吗?
- 自定义
swap
对哪些 classes 真的有益?
你应该遵循0的规则。
非资源管理 类 应具有默认值 move/copy/assign/destruction。
资源管理类应该只关心管理资源。
您的代码不管理资源。 copy/move 明智的一切都应该默认。
swap
是 move 的特例。如果您是资源管理类型,您可能还想编写它。但是,与大多数事情一样,它是一种优化,只有在证明它对性能有影响的情况下才应该进行此类优化。
它可能会对性能产生影响的一个例子是,如果您拥有一个永不为空的资源拥有类型并且该资源不是免费的,那么创建一个临时对象会不可避免地产生成本。 std::swap
默认情况下穿过一次性物体。自定义交换可以避免这种情况。
或者,一种类型,它是移动元素的廉价缓冲区;创建这么大的缓冲区可能会产生实际成本。
否则不做。并测试您是否这样做有帮助。自定义交换是针对特定情况的移动优化。
考虑使用移动构造函数 (MC)、移动赋值运算符 (MAO) 和自定义交换来保存一些资源(为简单起见,此处用整数成员变量表示)的自定义 class X
功能:
class X {
int i_;
public:
X(int i) : i_(i) { }
X(X&& rhs) = default; // move constructor
X& operator=(X&& rhs) = default; // move assignment operator
// X& operator=(X rhs) { std::swap(i_, rhs.i_); return *this; } // unifying assign. op.
friend void swap(X& lhs, X& rhs) { std::swap(lhs.i_, rhs.i_); }
int i() const { return i_; }
};
在以下基准代码中:
int main() {
int i1, i2;
std::cin >> i1 >> i2;
X x1(i1), x2(i2);
std::cout << x1.i() << x2.i();
swap(x1, x2);
std::cout << x1.i() << x2.i();
std::swap(x1, x2);
std::cout << x1.i() << x2.i();
}
swap(x1, x2)
只是 交换 2 个整数 。std::swap(x1, x2)
调用 1x MC 和 2x MAO.- 如果 MAO 被统一赋值运算符 (UAO) 替代,
std::swap(x1, x2)
调用 3x MC 和 2x UAO。
(使用 gcc/libstdc++ 测试)。
看来,自定义swap
非常有价值,因为它避免了很多函数调用。但是,当我查看此程序的反汇编输出时:https://godbolt.org/g/RMfZgL,MC、MAO、UAO、swap
和 std::swap
的所有功能都 直接内联到 main
(gcc/clang 带有 -O2
标志)。此外,程序中没有创建 X
类型的对象,它仅适用于机器代码级别的整数。
因此,在我看来,没有理由为像 X
这样的 class 提供自定义 swap
函数。
问题:
- 假设编译器将优化 MC 和
std::swap
内部的 MAO/UAO 调用是一种好习惯吗?或者,我应该为所有自定义 class 定义自定义swap
吗? - 自定义
swap
对哪些 classes 真的有益?
你应该遵循0的规则。
非资源管理 类 应具有默认值 move/copy/assign/destruction。
资源管理类应该只关心管理资源。
您的代码不管理资源。 copy/move 明智的一切都应该默认。
swap
是 move 的特例。如果您是资源管理类型,您可能还想编写它。但是,与大多数事情一样,它是一种优化,只有在证明它对性能有影响的情况下才应该进行此类优化。
它可能会对性能产生影响的一个例子是,如果您拥有一个永不为空的资源拥有类型并且该资源不是免费的,那么创建一个临时对象会不可避免地产生成本。 std::swap
默认情况下穿过一次性物体。自定义交换可以避免这种情况。
或者,一种类型,它是移动元素的廉价缓冲区;创建这么大的缓冲区可能会产生实际成本。
否则不做。并测试您是否这样做有帮助。自定义交换是针对特定情况的移动优化。