2009-09-06 72 views
6

我试图在C创建一个“稀疏”载体类++,像这样:重载操作符[]为稀疏向量

template<typename V, V Default> 
class SparseVector { 
    ... 
} 

在内部,它会被一个std::map<int, V>(表示,其中V是存储的值类型)。如果地图中没有元素,我们将假设它等于来自模板参数的值Default

但是,我无法重载下标操作符[]。我必须重载[]运算符,因为我将此类中的对象传递给了一个Boost函数,该函数预计[]能正常工作。

const版本很简单:检查索引是否在映射中,如果是,则返回其值,否则为Default

但是,非const版本要求我返回一个引用,这就是我遇到麻烦的地方。如果该值仅为,则为,我不需要(也不想)向地图添加任何内容;但如果是写成,我可能需要在地图中添加一个新条目。问题是,超载[]不知道是否值读取写入。它仅仅返回一个参考。

有什么办法可以解决这个问题吗?或者也许要解决它?

+2

boost :: mapped_vector <>应该做类似的事情 - 你可以研究它的想法(或者只是使用它)。 – 2009-09-06 17:10:19

+0

它不支持我的默认值,而且,我打算为二维矩阵做这件事,所以直接使用它是不可能的。但仍然是一个有用的参考! – Thomas 2009-09-06 19:08:09

回答

13

可能有一些非常简单的技巧,但除此之外,我认为operator[]只需返回可以从V分配的东西(并转换为V),而不一定是V &。所以我认为你需要返回一个超载的对象operator=(const V&),它会在你的稀疏容器中创建条目。

您必须检查Boost函数对其模板参数的作用,但用户定义的转换到V会影响可能的转换链,例如通过阻止同一个用户定义的转换链。

+0

这是唯一的解决方案。另一方面,代理对象也不完美 - 例如,如果'V'有任何重载的转换运算符,则代理将无法无缝地传播它们。 – 2009-09-06 17:22:32

+0

刚刚测试了助推矢量。看起来他们也是这样。有趣。 – 2009-09-06 17:38:51

+0

(并且'vector '也不是STL容器。) – sbi 2009-09-06 22:22:54

9

不要让非常量运算符&实现返回一个引用,而是一个代理对象。然后,您可以实现代理对象的赋值运算符,以区分从读写访问到读操作符[]的读访问。

下面是一些代码草图来说明这个想法。这种方法并不美观,但很好 - 这是C++。 C++程序员不会浪费时间参加选美比赛(他们也不会有机会)。 ;-)

template <typename V, V Default> 
ProxyObject SparseVector::operator[](int i) { 
    // At this point, we don't know whether operator[] was called, so we return 
    // a proxy object and defer the decision until later 
    return ProxyObject<V, Default>(this, i); 
} 

template <typename V, V Default> 
class ProxyObject { 
    ProxyObject(SparseVector<V, Default> *v, int idx); 
    ProxyObject<V, Default> &operator=(const V &v) { 
     // If we get here, we know that operator[] was called to perform a write access, 
     // so we can insert an item in the vector if needed 
    } 

    operator V() { 
     // If we get here, we know that operator[] was called to perform a read access, 
     // so we can simply return the existing object 
    } 
}; 
+0

有什么办法让一个类型可以转换为'const int&'和'int&',并且只要编译器可以使用不可写的左值,编译器就会选择它?我希望有一个代理对象返回一个引用到主对象加载的字段,然后让它的析构函数将字段复制回主对象,如果它可能已经改变,但我不明白如何以避免在只读访问上回写。 – supercat 2015-03-20 18:40:08

1

我想知道这个设计是否合理。

如果您想要返回一个引用,这意味着该类的客户端可以将调用operator[]的结果存储在引用中,并在以后的任何时间对其进行读取/写入。如果您没有返回引用,并且/或者每次处理特定索引时都不插入元素,他们怎么能这样做呢? (另外,我有这样的感觉,即标准需要一个合适的STL容器来提供operator[]来让该操作符返回一个引用,但我不确定这一点。)

你也许可以通过给你的代理也提供一个operator V&()(它会创建条目并指定默认值),但我不确定在某些情况下这不会打开另一个循环孔我还没有想过。

std::map通过指定该操作者的非const版本总是插入一个元素(并且不提供const版本在所有)解决了这个问题。

当然,你可以随时说这个不是一个现成的STL容器,并且operator[]不会返回用户可以存储的纯引用。也许这没关系。我只是好奇。

+0

关于设计的可靠性:它是一个通用向量的内存优化,用于特定情况 - 所以接口/合同/文档应该覆盖该内容。关于引用的有效性:即使stl迭代器也不总是有效的。 – xtofl 2009-09-06 20:11:54

+2

@xtofl:但是,STL容器非常仔细地定义了哪些操作可以使迭代器和/或对容器元素的引用无效。这个容器应该这样做,并且用户必须确保使用该类作为参数的任何模板都不会提出它不能满足的要求。 – 2009-09-06 20:37:59