7

私はgroup_by、SQLの同名のモデルをモデル化するという再帰マップクラスに取り組んでいます。

たとえば、gbは、、、、およびキータイプでグループ化されたポインタをこの順序で格納するgroup_byオブジェクトです。foostd::stringintchar

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

group_byat( I const& key )現在のレベルマップの内部を調べるために使用できるアクセサメソッドを提供します。at()より深いマップを取得するための呼び出しを連鎖させることは問題なく機能します。

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

問題

連鎖することなく、1回の呼び出しでより深いマップをすべて取得できるat()calledの代替案を作成したいと思います。at_variadic( Args const& ...args )

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

しかし、私はいくつかの問題に直面しています。まず、可変個引数に依存するため、戻り型を指定する方法がわかりません。多分decltype()、どういうわけか使用しますか?

ワーキングアンサー

以下のEcatmurの回答は、優れたアプローチの概要を示しています。

コンパイラを満足させるためにのターミナルケースをgroup_by<>いじくり回さなければなりませんでしたが、以下のコードは、Ecatmurの回答に大きく基づいており、gcc4.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

1 に答える 1

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...、読みやすいはずです。

于 2013-02-18T11:35:55.320 に答える