如何在operator []上分隔get和set访问?

时间:2014-03-12 00:46:38

标签: c++ operator-overloading rvalue

编辑我通过引用rvalues引起了一些混乱。我提供了一个我想在底部做的事情的例子。

我想分开这两个电话:

obj[key] = value;
value = obj[key];

我尝试使用两个operator []重载,希望const方法专门用于rvalues,而非const版本将专门用于左值调用。

const ValueType& operator[] (const KeyType& key) const;
ValueType& operator[] (const KeyType& key);

但是我的const版本只在this为const时被调用。

我尝试使用覆盖op=op ValueType

的包装器
BoundValue<KeyType, ValueType> operator[] (const KeyType& key)

// later in BoundValue
operator ValueType() const
const V& operator= (const V& value)

这非常有效...直到我希望op[]隐式地为模板参数演绎(不允许)进行演绎。

// example failure of wrapper
// ValueType is string, std::operator<< from string is a template method
cout << obj[key];

对于这个特定情况,我可以定义自己的ostream方法。

ostream& operator<< (ostream& os, const BoundValue<K, V>& bv);

但是我的类型同样会在任何用户定义的模板参数中失败(似乎很可能)。

我可以想到两种解决方法,这两种方法都大大降低了我试图提供的语法糖的甜度:

// example where ValueType is string
cout << static_cast<string>(obj[key]);

,或者

// added to BoundValue
const ValueType& Cast() const

// later in use
cout << obj[key].Cast();
所需用例的

示例

Map<int, string> m;
m[1] = "one";

try
{
    string one = m[1];
    cout << "one is set: " << one << endl;
    string two = m[2];
    cout << "two is set: " << two << endl;
}
catch (...)
{
    cout << "An exception was thrown" << endl;
}

预期产出:

one is set: one
An exception was thrown

如何/为什么抛出异常?我可以检查密钥是否存在,如果不存在则抛出。如果我无法将get与set分开,我不知道何时允许访问密钥。

2 个答案:

答案 0 :(得分:1)

operator[]返回一个引用(类似)类型,该类型实现解除引用(get)和变异(set)操作,operator*()operator=()。理论上,至少,你不需要返回一个真实的参考,只要你愿意做所需的工作,使你返回的值(大多数)像一个参考。因此,您可以使用在地图中存在的密钥的情况下存储指向pair<const Key, Val>的指针的类型,否则使用不存在的Key的副本。在后一种情况下,解引用运算符会抛出异常,变异运算符会将键移动到映射中。

保留密钥的副本以便在必要时懒惰地将它们添加到地图中有点贵,但如果你真的想要这个功能,它不一定是令人发指的。一种可能的优化方法是始终将键添加到地图中,但不构建匹配的Val类型,也不允许map返回Key值。一种简单的实现方法是为每个键值对分配一个位,指示该键是否实际在映射中。和以前一样,尝试取消引用指向键值对的指针并不真正在映射中会引发异常,而值的变化只会构造新值并设置有效位。参考运算符的析构函数实际上会从映射中删除无效键,但假设此操作很少发生,因为根据定义,它将是例外的。不幸的是,强大的实现并不那么简单,因为您需要考虑同时存在多个对应于同一个不存在的键的引用对象的可能性。如果值类型足够大,可以在其上覆盖引用计数,则可以使用引用计数方案,但我认为除非仔细记录operator[],否则您需要担心数据争用。 (引用计数不是很简单,因为密钥有可能因其中一个引用被变异而变得有效。幸运的是,没有必要保持有效密钥的计数,因此析构函数只需要在递减引用计数和/或删除无效密钥之前检查有效性。)

上述方法存在一些缺点。一个是C ++没有单个变异操作符;它实际上有很多种(+=-=<<=等等,并且不会忘记++--)。所以会有相当多的样板。

更严重的是,程序保留operator[]返回的值作为引用对象是完全合法的,并且许多程序员将假设返回的引用是简单的Val&。通常,此类代码的目的是能够执行多个突变而无需执行多次查找。除了类型问题,这些天使用auto轻松解决,这将“可能正常”,但延迟插入可能会产生一些意外。

答案 1 :(得分:0)

你有没有尝试过:

     Friend ostream& operator<< (ostream& os, const BoundValue<K, V>& bv);

当操作符在另一个类中重载并且您想要在类中重载操作符时,必须将您的函数声明为友元函数。就像使用运算符&lt;&lt;在&#34; fstream&#34;库

中重载