如何正确交换含引用成员的foo类对象?std::swap失效问题求解
解决带引用成员类的
std::swap异常问题 我刚折腾了一整天解决了一个类似的bug,正好碰到你这个场景——带引用成员的foo类用std::swap后出现了异常结果,先给你拆解问题根源,再给你正确的解决方案。
问题根源分析
std::swap的默认实现逻辑是:创建临时对象,通过拷贝构造和赋值操作完成交换。但你的foo类里的operator=只是把引用指向的值做了赋值(也就是让this->i_指向的变量等于that.i_指向的变量),而引用本身一旦绑定就无法更改指向。
当你调用std::swap(f1, f2)时,默认流程大概是这样:
- 创建临时
foo对象tmp = f1(tmp的引用指向x) f1 = f2(f1的引用还是指向x,但把x的值改成了y的值100)f2 = tmp(f2的引用还是指向y,但把y的值改成了tmp引用的x的值——也就是已经变成100的x)
最后x和y都变成100,两个foo对象的引用自然都输出100,这就是你看到的异常结果。
正确的交换方案
因为引用无法重新绑定,我们需要实现的是交换两个foo对象引用指向的变量的值,而不是交换foo对象本身的引用成员。我们可以为foo类自定义一个友元swap函数,利用ADL(参数依赖查找)让编译器优先使用这个自定义版本:
修改后的foo类代码
struct foo { inline foo(int& i) : i_(i) { } inline foo& operator=(const foo& that) { if (this != &that) { i_ = that.i_; // 保持原有赋值逻辑:拷贝引用指向的值 } return *this; } // 自定义swap函数:交换两个foo对象引用指向的int变量的值 inline friend void swap(foo& lhs, foo& rhs) noexcept { std::swap(lhs.i_, rhs.i_); // 这里交换的是int值,而非引用本身 } inline friend std::ostream& operator<<(std::ostream& os, const foo& f) { os << f.i_; return os; } private: foo(); int& i_; };
修改后的运行结果
当你用原来的测试代码运行时,结果会变成符合预期的:
before swapping x = 10 y = 100 f1 = 10 f2 = 100 after swapping x = 100 y = 10 f1 = 100 f2 = 10
重要提醒
std::swap并非通用解决方案:
- 它的默认实现依赖于类的拷贝构造和赋值运算符,对于包含引用、指针、动态资源的类,默认swap往往不符合业务逻辑,甚至会导致错误。
- 当你的类包含特殊成员(比如引用、需要手动管理的资源)时,一定要自定义合适的swap函数,确保交换逻辑符合你的预期。
内容的提问来源于stack exchange,提问作者Ngoc Linh
相关产品推荐
相关产品推荐

