1

タイプ消去を使用してマップ ラッパーを実装するライブラリを C++ で作成しています。ラッパーは、このすばらしい記事のように構成されています: 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(); }
};

このマップを完成したライブラリの一部として公開するか、ヘルパー クラスとして隠すかはわかりませんが、どちらにしても、代入演算子をどうするかは考え中です。

現在、私は次のようなものを持っています:

AnyMap& AnyMap::operator=(const AnyMap& other) {
    delete mapConcept;
    mapConcept = other.mapConcept->clone();
    return *this;
}

これは、たとえば STL のマップと Boost の unordered_map を使用して 2 つのマップを作成し、一方を他方に割り当てると、両方の基になるマップ タイプが同じになることを意味します。

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

に割り当てられたマップの内容が期待どおりであるため、これはある程度理にかなっています。ただし、通常、マップ タイプは、デフォルト値を持つタイプ引数の 1 つ (Boost::unordered_map の Hash など) によって異なると思われます。したがって、基になるマップ タイプを保持する必要があるかもしれません。私は、これは次のようなもので行うことができると思います:

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を使用しました。

それで、あなたたちはどう思いますか?私は後者のアプローチに傾いていますが、どちらの側の議論も聞きたいです。

前もって感謝します!

編集: ここに反対の最初の引数があります (おそらく、これがどれほど重要か教えてください): 1 つの述語がマップ内の 2 つのキーを区別し、もう一方の述語がマップ内の 2 つのキーを区別する場合、1 つのマップ タイプの内容が別のマップに 1 対 1 でマップされる可能性があります。はそれらを同じと見なします。

4

1 に答える 1

1

マップラッパーに値のセマンティクスを持たせたいですか? これにより、コピーの深さが決まります。いずれにせよ、 の実装はother.mapConcept->clone()ポリモーフィックであり (結局のところ、これは C++ の型消去手法の本質です)、次のような MapModel サブクラスの呼び出しへのディスパッチになります。

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

したがって、すべてが ActualMapType のコピー コンストラクターの動作に要約されます (メンバー変数mがコピーになるため)。

この手法は値のセマンティクスを取得するために発明されたものなので、その機能を保持することは最小サプライズの原則と一致していると思います。また、インターフェイスが固定されているのもポイントです。実装 (STL やブーストなど) は設計上無関係であるため、特定のオブジェクト インスタンスで実装を保持しようとしてもほとんど意味がありません。

ちなみに、「標準」ケースの operator= の実装は例外セーフではありません。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; }
于 2013-01-12T22:35:19.247 に答える