2013-02-18 6 views
7

私はgroup_byという名前の再帰的なマップクラスを作成しています。例えば連鎖したメソッド呼び出しを置き換えるvariadicメソッドを書く方法は?

は、GBの順に、std::stringint、そしてcharキータイプによってグループ化fooへのポインタを格納するgroup_byオブジェクトです。

group_by<foo,std::string,int,char> gb; 

group_by現在のレベルのマップの内部を見るために使用することができるat(I const& key)アクセサ方法を提供します。より深いマップを取得するためにat()を呼び出すと、正常に動作します。

auto& v = gb.at(k1).at(k2).at(k3).get_vec(); 

私は、連鎖なしで、すべて1回の呼び出しで深くマップを取得することができますat_variadic(Args const& ...args)と呼ばれるat()の代替を作成したいと考えPROBLEM

auto& w = gb.at_variadic(k1, k2); 
auto& x = gb.at_variadic(k1, k2, k3); 

しかし、私はいくつかの問題を抱えています。まず第一に、返り値の型を指定する方法はわかりません。なぜなら、変数の引数に依存するからです。おそらくdecltype()を使用しますか?

WORKING ANSWER

Ecatmur's answer below良いアプローチを概説しました。

私はコンパイラを幸せにするために、端末ケースgroup_by<>で遊ばなければなりませんでしたが、Ecatmurの答えに大きく依存する以下のコードは、gcc 4.7.2でうまく動作しているようです。

#include <cassert> 
#include <map> 
#include <vector> 
#include <iostream> 

template< typename T, typename... Args > 
struct group_by 
{ 
    using child_type = T; 

    std::vector<T*> m_vec; 

    void insert(T* t) 
    { 
     m_vec.push_back(t); 
    } 

    child_type& 
    at(size_t i) 
    { 
     return *m_vec[i]; 
    } 
}; 

template< typename T, typename I, typename... Args > 
struct group_by<T,I,Args...> 
{ 
    using child_type = group_by<T,Args...>; 

    std::map<I,child_type> m_map; 

    void insert(T* t) 
    { 
     m_map[ *t ].insert(t); 
    } 

    child_type& at(I const& key) 
    { 
    return m_map.at(key); 
    } 

    template<typename... Ks> 
    auto 
    at(I const& i, Ks const&...ks) 
    -> decltype(m_map.at(i).at(ks...)) 
    { 
     return m_map.at(i).at(ks...); 
    } 
}; 

// ----------------------------------------------------------------------------- 

struct foo 
{ 
    std::string s; 
    int   i; 
    char   c; 

    operator std::string() const { return s; } 
    operator int  () const { return i; } 
    operator char  () const { return c; } 

    bool operator==(foo const& rhs) const 
    { 
     return s==rhs.s && i==rhs.i && c==rhs.c; 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
    foo f1{ "f1", 1, 'z' }; 
    foo f2{ "f2", 9, 'y' }; 
    foo f3{ "f3", 3, 'x' }; 
    foo f4{ "f1", 4, 'k' }; 

    group_by<foo,std::string,int,char> gb; 

    gb.insert(&f1); 
    gb.insert(&f2); 
    gb.insert(&f3); 
    gb.insert(&f4); 

    std::string k1{ "f1" }; 
    int   k2{ 1 }; 
    char  k3{ 'z' }; 

    auto& a = gb.at(k1).at(k2).at(k3).at(0); 
    auto& b = gb.at(k1).at(k2).m_map; 
    auto& c = gb.at(k1).m_map; 
    auto& d = gb.at(k1, k2).m_map; 
    auto& e = gb.at(k1, k2, k3).m_vec; 
    auto& f = gb.at(k1, k2, k3, 0); 

    assert(a==f1); 
    assert(b.size()==1); 
    assert(c.size()==2); 
    assert(d.size()==1); 
    assert(e.size()==1); 
    assert(f==f1); 

    return 0; 
} 

答えて

4

チェーンメソッド呼び出しあなたが再帰的にatを実装する必要があるので、基本的に再帰的である:

child_type& at(I const& key) { 
    return m_map.at(key); 
} 

template<typename J, typename... Ks> 
auto at(const I &i, const J &j, const Ks &...ks) 
-> decltype(m_map.at(i).at(j, ks...)) { 
    return m_map.at(i).at(j, ks...); 
} 

注意をatは、少なくとも1つの引数を必要とするため、可変長フォームは、少なくとも2つのパラメータを取ること。これは、sizeof...にディスパッチするよりも実装がはるかに簡単で、読みやすくする必要があります。

+0

+1 tyvm - これを今すぐ試します – kfmfe04

+0

これを試しましたか?それはコンパイルされていないようです... –

+0

@AndyProwlあなたは正しい - 私はそれを動作させるためにそれを再生しなければならなかった。今すぐ掃除する。完了したらOPを更新します。彼の答えは、私がそれを働かせるのに十分だった。 – kfmfe04