如何根据左右值调用场景重载函数调用operator()?
C++中operator()的const重载按需调用(无代理类低成本实现)
需求描述
希望为Matrix类的函数调用运算符operator()实现以下行为:
- 当对象作为左值(表达式左侧)时,始终调用非const版本(写入
current数组) - 当对象作为右值(表达式右侧)时,始终调用const版本(读取
previous数组)
当前问题:非const实例在表达式右侧时,编译器会优先选择非const版本的operator(),无法触发读取previous数组的逻辑。已知代理类可实现该需求,但存在构造代理对象的开销;也可通过const getter解决,但希望深入了解运算符重载的实现方式。
示例代码:
#include <iostream> #include <vector> class Matrix { private: const int m_width; const int m_height; std::vector<double> m_arr1; std::vector<double> m_arr2; std::vector<double>* m_curr; std::vector<double>* m_prev; public: Matrix(const int width, const int height) : m_width{width}, m_height{height}, m_arr1(width*height, 0.0), m_arr2(width*height, 0.0), m_curr{&m_arr1}, m_prev{&m_arr2} {} // 非const版本:写入current数组 double& operator()(const int i, const int j) { std::cout << "write to (" << i << ", " << j << ")" << std::endl; return (*m_curr)[m_width*j + i]; } // const版本:读取previous数组 const double& operator()(const int i, const int j) const { std::cout << "read from (" << i << ", " << j << ")" << std::endl; return (*m_prev)[m_width*j + i]; } void swap() { std::cout << "swap" << std::endl; std::vector<double>* temp{m_curr}; m_curr = m_prev; m_prev = temp; } }; int main() { Matrix A{2, 2}; A(1, 0) = -1; // 期望:写入current数组(m_arr1),实际符合预期 A.swap(); // 交换curr和prev指针 A(1, 0) = 3 + A(1, 0); // 期望:右侧读取previous数组(m_arr1),左侧写入current数组(m_arr2);实际两次都调用非const版本 return 0; }
无代理类的低成本解决方案
核心问题是C++重载决议规则:非const对象调用成员函数时,编译器会优先选择非const版本。要让表达式右侧的调用触发const版本,需显式引导编译器选择const重载,以下两种方式均为零运行时开销:
1. 显式const类型转换
在表达式右侧调用operator()前,将对象转换为const Matrix&,强制编译器选择const版本:
int main() { Matrix A{2, 2}; A(1, 0) = -1; A.swap(); // 显式转换为const引用,触发const版本operator() A(1, 0) = 3 + static_cast<const Matrix&>(A)(1, 0); return 0; }
运行输出:
write to (1, 0) swap read from (1, 0) write to (1, 0)
2. 添加const引用辅助函数
在Matrix类中添加一个返回自身const引用的辅助函数,通过该函数间接调用const版本的operator(),代码可读性更好:
class Matrix { // ... 现有成员 ... public: // 返回自身的const引用,无运行时开销 const Matrix& as_const() const noexcept { return *this; } // ... 现有成员 ... }; int main() { Matrix A{2, 2}; A(1, 0) = -1; A.swap(); // 通过as_const()获取const引用,触发const版本operator() A(1, 0) = 3 + A.as_const()(1, 0); return 0; }
此方式与类型转换效果完全一致,但代码更直观,无需记忆强制转换语法。
原理说明
上述两种方式均仅涉及编译期类型转换,不会产生任何运行时对象构造或额外开销,完全满足"低成本"的要求。它们通过改变调用者的类型(从非const引用变为const引用),让重载决议逻辑选择const版本的operator()。
内容的提问来源于stack exchange,提问作者Petri Hirvonen
相关产品推荐
相关产品推荐

