代码之家  ›  专栏  ›  技术社区  ›  Tomaka17

C++0x:通过函数遍历元组

  •  3
  • Tomaka17  · 技术社区  · 16 年前

    我有一个函数名为 _push 它可以处理不同的参数,包括元组,并且应该返回推送元素的数量。

    例如, _push(5) 应将“5”推到堆栈上 stack of lua )返回1(因为推了一个值),而 _push(std::make_tuple(5, "hello")) 应该按“5”和“你好”,然后返回2。

    我不能简单地用 _push(5, "hello") 因为我有时会用 _push(foo()) 我想允许 foo() 返回元组。

    无论如何,我无法使它与tuples一起工作:

    template<typename... Args, int N = sizeof...(Args)>
    int _push(const std::tuple<Args...>& t, typename std::enable_if<(N >= 1)>::type* = nullptr) {
     return _push<Args...,N-1>(t) + _push(std::get<N-1>(t));
    }
    
    template<typename... Args, int N = sizeof...(Args)>
    int _push(const std::tuple<Args...>& t, typename std::enable_if<(N == 0)>::type* = nullptr) {
     return 0;
    }
    

    假设你想推一个 tuple<int,bool> . 这就是我期望它工作的方式:

    • _push<{int,bool}, 2> 被调用(第一个定义)
    • _push<{int,bool}, 1> 被调用(第一个定义)
    • _push<{int,bool}, 0> 被调用(第二个定义)

    但是,使用G++4.5(我拥有的唯一一个支持可变模板的编译器),我会得到一个关于 _push<Args...,N-1>(t) (第3行)表示找不到要调用的匹配函数(没有任何详细信息)。我在没有“…”的情况下尝试了,但是我得到了另一个错误,说参数包没有扩展。

    我怎么修这个?

    PS:我知道你可以使用模板结构来完成这项工作(实际上这是我以前做的),但是我想知道如何使用函数来完成这项工作。

    PS 2:PS2解决了,谢谢GMAN

    6 回复  |  直到 15 年前
        1
  •  6
  •   GManNickG    15 年前

    我没有编译器来测试这些,所以您必须报告任何问题。

    下面应该允许您遍历调用函数的元组。它是基于你的逻辑,有一些微小的变化。( N 是一个 std::size_t ,这是允许的第一个参数 Args (和) Func )在进一步的调用中,它只调用一些函数,而不是执行特定的任务)。没有什么太激烈的:

    namespace detail
    {
        // just to keep things concise and readable
        #define ENABLE_IF(x) typename std::enable_if<(x)>::type
    
        // recursive case
        template <std::size_t N, typename... Args, typename Func>
        ENABLE_IF(N >= 1) iterate(const std::tuple<Args...>& pTuple, Func& pFunc)
        {
            pFunc(std::get<N - 1>(pTuple));
    
            iterate<N - 1>(pTuple, pFunc);
        }
    
        // base case
        template <std::size_t N, typename... Args, typename Func>
        ENABLE_IF(N == 0) iterate(const std::tuple<Args...>&, Func&)
        {
            // done
        }
    }
    
    // iterate tuple
    template <typename... Args, typename Func>
    Func iterate(const std::tuple<Args...>& pTuple, Func pFunc)
    {
        detail::iterate<sizeof...(Args)>(pTuple, pFunc);
    
        return pFunc;
    }
    

    假设一切正常,那么您只需要:

    struct push_lua_stack
    {
        // constructor taking reference to stack to push onto
        // initialize count to 0, etc....
    
        template <typename T>
        void operator()(const T& pX)
        {
            // push pX onto lua stack
            ++count;
        }
    
        std::size_t count;
    };
    

    最后:

    std::size_t pushCount = iterate(someTuple, push_lua_stack()).count;
    

    如果一切都有道理,请告诉我。


    由于某些原因,您似乎真的非常反对结构,所以只需创建如下函数:

    template <typename T>
    void push_lua(const T& pX)
    {
        // push pX onto lua stack
    }
    

    并将所有内容更改为专门调用该函数:

    namespace detail
    {
        // just to keep things concise and readable
        #define ENABLE_IF(x) std::enable_if<(x)>::type* = nullptr
    
        // recursive case
        template <std::size_t N, typename... Args>
        typename ENABLE_IF(N >= 1) iterate(const std::tuple<Args...>& pTuple)
        {
            // specific function instead of generic function
            push_lua(std::get<N - 1>(pTuple));
    
            iterate<N - 1>(pTuple);
        }
    
        // base case
        template <std::size_t N, typename... Args, typename Func>
        typename ENABLE_IF(N == 0) iterate(const std::tuple<Args...>&, Func&)
        {
            // done
        }
    }
    
    // iterate tuple
    template <typename... Args>
    void _push(const std::tuple<Args...>& pTuple)
    {
        detail::iterate<sizeof...(Args)>(pTuple);
    }
    

    但不知道为什么要避免使用一般的功能,或者反对结构。


    多好的多态lambda。抛弃公用事业 push_lua_stack 上课,然后写:

    std::size_t count = 0;
    
    iterate(someTuple, [&](auto pX)
                        {
                            // push onto lua stack
                            ++count;
                        });
    

    哦,好吧。

        2
  •  1
  •   Tomaka17    16 年前

    我用一些黑客程序解决了这个问题。代码如下:

    template<typename... Args, int N = sizeof...(Args)>
    int _push(const std::tuple<Args...>& t, std::integral_constant<int,N>* = nullptr, typename std::enable_if<(N >= 1)>::type* = nullptr) {
        return _push(t, static_cast<std::integral_constant<int,N-1>*>(nullptr)) + _push(std::get<N-1>(t));
    }
    template<typename... Args, int N = sizeof...(Args)>
    int _push(const std::tuple<Args...>& t, std::integral_constant<int,N>* = nullptr, typename std::enable_if<(N == 0)>::type* = nullptr) {
        return 0;
    }
    

    如果你能找到更好的方法,就别犹豫了。

        3
  •  0
  •   max    15 年前

    如果你想用一个函数迭代一个元组,你可以用一点样板文件来实现。其思想是建立一个与元组索引对应的可变整数列表,然后使用std::get访问值。迅速地:

    template<int...> struct indices;
    
    // constructs an index list for a tuple e.g. indices<0, 1, 2, 3> 
    template<class ... Args> some_index_type make_indices();
    

    然后可以展开这样的元组:

    template<class Args...> void foo(const std::tuple<Args...>& tup) {
        foo(tup, make_indices<Args...>());
    }
    
    template<class Args..., int...I> void foo(const std::tuple<Args...>& tup,
                                                indices<I...> ){
       bar( std::get<I>(tup)... );
    }
    

    这将扩展tuple内容并将其馈送到函数栏。

    希望这有帮助:)

        4
  •  0
  •   sellibitze    15 年前

    这是我能想到的最简单的解决方案之一。我用GCC 4.4成功地测试了它:

    #include <iostream>
    #include <tuple>
    
    template<class T>
    void push(T x)
    {
      using namespace std;
      cout << x << '\n';
    }
    
    template<int Remaining>
    struct push_tuple_helper
    {
      template<class... Args>
      static void doit(std::tuple<Args...> const& t)
      {
        push(std::get<sizeof...(Args)-Remaining>(t));
        push_tuple_helper<Remaining-1>::doit(t);
      }
    };
    
    template<>
    struct push_tuple_helper<0>
    {
      template<class... Args>
      static void doit(std::tuple<Args...> const& t) {}
    };
    
    template<class... Args>
    void push(std::tuple<Args...> t)
    {
      push_tuple_helper<sizeof...(Args)>::doit(t);
    }
    
    int main()
    {
      using namespace std;
      push( 42 );
      cout << "---\n";
      push( "Hello World" );
      cout << "---\n";
      push( make_tuple(42,3.14,"foo") );
    }
    
        5
  •  0
  •   Roger Pate    15 年前

    不要让一个函数做两件不同的事情,而是将关注点分开:

    _push(value, ...); // using variadic templates for 1 to N values
    _push_seq(sequence); // always requires a sequence, never a value
    

    那么问题就不再存在了。对于是推送一个包含多个值的项目(我不熟悉Lua,但我知道它有一个主容器类),还是从一个序列推送多个项目,您没有含糊不清的地方。

    重命名函数可能会有所帮助:

    _append(value, ...); // _push(value, ...) above
    _extend(sequence); // _push(sequence) above
    

    为了进行比较,请考虑std::vector如何始终对一个项(_append)使用push-back,对多个项使用insert(_extend);它不会尝试混合这两个概念。

        6
  •  -1
  •   KitsuneYMG    15 年前

    编辑每个的常规

    template<size_t N>
    struct for_each_impl
    {
      template<typename Func, typename Tuple>
      void operator()(Func func, Tuple const& arg)
      {
        for_each_impl<N-1>()(func, arg );
        return func( std::get<N-1>( arg ) );
      }
    };
    
    template<>
    struct for_each_impl<1>
    {
      template<typename Func, typename Tuple>
      void operator()(Func func, Tuple const& arg)
      {
        func( std::get<0>( arg ) );
      }
    };
    
    template<typename Func, typename ... Args>
    void for_each( Func func, std::tuple<Args...>const& tup )
    {
      for_each_impl< sizeof...(Args)>()( func, tup );
    }
    

    示例用法

    struct printer {
        ostream& out;
        explicit printer( ostream& out=std::cout ) : out(out) { }
    
        template<typename T>void operator()(T const&t) const { out<<t<<", "; }
    };
    
    cout << '[';
    for_each( printer(cout), make_tuple(0,.1,"hello") );
    cout << ']';