【问题标题】:How to write a variadic method which replaces chained method calls?如何编写替换链式方法调用的可变参数方法?
【发布时间】:2013-02-18 11:02:23
【问题描述】:

我正在开发一个名为 group_by 的递归映射类,它对 SQL 同名进行建模。

例如,gb 是一个group_by 对象,它将存储指向foo 的指针,按std::stringintchar 键类型按顺序分组。

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

group_by 提供了一个at( I const&amp; key ) 访问器方法,可用于查看当前关卡地图。链接 at() 调用以检索更深的地图工作正常。

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

问题

我想创建一个名为 at_variadic( Args const&amp; ...args )at() 的替代品,它可以在一次调用中检索更深层的地图,而无需链接。

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

但是,我遇到了一些问题。首先,我不知道如何指定返回类型,因为它取决于可变参数。也许使用decltype(),不知何故?

有效答案

Ecatmur's answer below 概述了一个很好的方法。

我不得不使用group_by&lt;&gt; 的终端情况来让编译器满意,但下面的代码很大程度上基于 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;
}

【问题讨论】:

    标签: c++ templates c++11 variadic-functions


    【解决方案1】:

    链式方法调用本质上是递归的,所以需要递归实现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... 上调度要容易得多,并且应该更容易阅读。

    【讨论】:

    • @AndyProwl 你是对的 - 我必须玩它才能让它工作。现在清理它。完成后将更新 OP。他的回答对我来说已经足够了。
    • @kfmfe04:好的,请在完成后发布正确的代码。我很好奇:-)
    • @AndyProwl 确定 - 现在好了 - 享受吧!
    • @AndyProwl 对,这取决于至少有两层嵌套。最简单的解决方法是仅将其添加到 template&lt; typename T, typename I, typename J, typename... Args &gt; struct group_by&lt;T,I,J,Args...&gt; 及以上。
    • @kfmfe04 decltype(at(i).at(ks...)) 应该可以工作,因为这将影响公共基本情况。或者decltype(std::declval&lt;std::map&lt;I,child_type&gt;&gt;().at(i).at(ks...)),但这不太优雅。
    猜你喜欢
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 2018-04-29
    • 2020-10-22
    • 1970-01-01
    • 2020-10-16
    • 2010-11-04
    相关资源
    最近更新 更多