我看到了其他一些十年前编写的 QnAs,它们实现了单独的下标运算符重载以进行读写。
对于即:
class String{
public:
class Cref;
Cref operator[] (int i);
char operator[] (int i) const;
...
};
class String::Cref{
friend class String;
public:
operator char() { return s.read(i);}
void operator=(char c){s.write(i,c);}
...
};
class X;
class Proxy {
X* object;
Key key;
public:
Proxy(X* object, Key key): object(object), key(key) {}
operator V() const { return object->read(key); }
void operator=(V const& v) { object->write(key, v); }
};
class X {
public:
V read(key) const;
void write(key, V const& v);
Proxy operator[](Key key) { return Proxy(this, key); }
V operator[](Key key) const { return this->read(key); }
};
class CountingProxy
{
public:
CountingProxy<T>& operator=(const T& o)
{
cout << "Is writing\n";
counter_++;
ref_ = o;
return *this;
}
operator T()
{
cout << "Is reading\n";
return ref_;
}
...
}
class Array
{
public:
CountingProxy<T> operator[] (int index) {
return CountingProxy<T>(changes, data[index]);
}
T operator[] (int index) const {
cout << "Is reading\n";
return data[index];
}
...
}
...所有 3 种情况都定义了不返回代理类(或结构)的下标运算符重载的
const
变体。
Cref operator[] (int i);
char operator[] (int i) const; // <---
Proxy operator[](Key key);
V operator[](Key key) const; // <---
CountingProxy<T> operator[] (int index);
T operator[] (int index) const; // <---
看着这些,我以为那些const重写是在不写的时候调用的。
但是,当使用我自己的实现来实现简单的
std::map
包装器时:
#include <iostream>
#include <map>
#include <string>
using namespace std;
struct Proxy {
string& s;
Proxy(string& s) : s(s) {}
operator string &() const {
cout << "calling string() (read-only)" << endl;
return s;
}
void operator= (const char* val) {
cout << "calling = (write-only)" << endl;
s = val;
}
};
// Method for cout support
ostream& operator<<(ostream& os, const Proxy& self)
{
// In real case, this can be replaced to `os << self.s;`.
// But to log string() on stdout, making explicit conversion.
string converted_str = self;
os << converted_str;
return os;
}
struct SubscriptOverloadDemo {
map<string, string> some_map;
Proxy operator[] (const char* key) {
cout << "calling []" << endl;
return Proxy(some_map[key]);
}
const string& operator[] (const char* key) const {
cout << "calling const []" << endl;
return some_map.at(key);
}
};
int main() {
SubscriptOverloadDemo t;
// Proxy
t["a"] = "asdf";
// const string&?
const string& s = t["a"];
cout << s << endl;
// Proxy
t["a"] = "qwer";
cout << t["a"] << endl;
return 0;
}
...从不调用
const string& operator[]
,并且始终调用代理。
calling []
calling = (write-only)
calling []
calling string() (read-only)
asdf
calling []
calling = (write-only)
calling []
calling string() (read-only)
qwer
在编译器资源管理器中查看 MSVC 和 gcc 的程序集时,也不会编译覆盖。
最终我尝试自己创建 JSON 对象(出于某种原因),但这种行为让我感到困惑,这是我的实现的错误还是按预期工作。
不存在实施错误。而是你从来没有给过调用
const T&
操作符[]的机会。它将仅应用于 const SubscriptOverloadDemo
对象。
例如,尝试:
const SubscriptOverloadDemo t2{{ {"a", "asdf"}}};
std::cout << t2["a"] << endl; // It will print "calling const []"