我正在用C++写一个库,为此我实现了带有类型擦除的 map 包装器。包装器的结构如这篇精彩的文章http://www.cplusplus.com/forum/articles/18756/所示。

TLDR:

template<typename K, typename V>
class AnyMap
{
    class MapConcept
    {
    public:
        // Lots of pure virtual methods...
        // These mimic the intersection of STL and Boost maps' interfaces
        // Example:
        virtual size_type size() = 0;
    };

    template<typename ActualMapType>
    class MapModel : MapConcept
    {
        ActualMapType m;
    public:
        // Implementations of the parent's virtual methods...
        // These basically just call the same method on member m.
        // Example:
        size_type size()  { return m.size(); }
    };

    MapConcept* mapConcept;

public:
    // Again, the STL and Boost maps' interface methods
    // Example:
    size_type size()  { return mapConcept->size(); }
};

我不确定是否要将此 map 公开为完成的库的一部分,还是将其作为帮助程序类使用,但是无论哪种方式,我都想知道如何使用赋值运算符。

目前,我有这样的事情:
AnyMap& AnyMap::operator=(const AnyMap& other) {
    delete mapConcept;
    mapConcept = other.mapConcept->clone();
    return *this;
}

这意味着,如果我创建两个带有STL的 map 和Boost的unordered_map的 map ,然后将一个分配给另一个,则这两个 map 现在都具有相同的 map 类型。
std::map<string, int> stlMap;
boost::unordered_map<string, int> boostMap;
// insert some stuff into maps
AnyMap<string, int> stlAnyMap( stlMap );
AnyMap<string, int> boostAnyMap( boostMap );

stlAnyMap = boostAnyMap;
// now stlAnyMap has a copy of boostMap

因此,这是有道理的,因为分配给 map 的内容是预期的。但是,我怀疑通常映射类型会以具有默认值的类型参数之一(例如Boost::unordered_map中的Hash)不同。因此,也许它应该保留基础 map 类型。我认为,这可以通过以下方式完成:
AnyMap& AnyMap::operator=(const AnyMap& other) {
    mapConcept->clear();
    mapConcept->insert( other.mapConcept->begin(), other.mapConcept->end() );
    return *this;
}

由于模板插入方法,这应该可以工作:
template <class InputIterator>
  void insert (InputIterator first, InputIterator last);

顺便说一下,如果有人想知道我如何处理迭代器:我使用了Thomas Becker的any_iterator-http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/any_iterator.html

那么,你们怎么看?我倾向于后一种方法,但是我想听听任何一方的意见。

提前致谢!

编辑:这是反对的第一个论点(也许,你可以告诉我这有多重要):如果一个谓词可区分 map 中的两个键,而另一个谓词则可将一个 map 类型的内容现在一对一地映射到另一张 map 认为它们相同。

最佳答案

您是否希望 map 包装器具有值语义?这将确定副本的深度。无论如何,other.mapConcept->clone()的实现将是多态的(毕竟,这是C++类型擦除技术的本质),并导致分派(dispatch)给MapModel子类中的调用,如下所示

virtual MapModel *clone() { return new MapModel(m); } // covariant return type

因此,一切都归结为ActualMapType的副本构造函数所做的事情(因为成员变量m将是副本)。

由于该技术是为了获得值(value)语义而发明的,因此我认为保留该功能与“最少惊喜”的原理是一致的。而且,关键是要有一个固定的接口(interface)。实现(STL或boost或其他任何东西)与设计无关,因此尝试将实现保留在任何特定对象实例中毫无意义。

顺便说一句,对于“标准”情况,您对operator =的实现也不异常(exception)。 copy-and-swap 的习惯用法(也许使用自定义的swap()方法)效果更好
AnyMap( AnyMap const& other )
: mapConcept( other.mapConcept ? other.mapConcept->clone() : 0)
{}

AnyMap& operator= ( AnyMap rhs ) // note temporary: invokes copy constructor
{ std::swap( mapConcept, rhs.mapConcept ) ; return *this; }

关于c++ - 类型删除的 map 的拷贝(赋值)应有多深,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14297781/

10-09 05:34
查看更多