C ++ 0x:使用函数迭代元组

时间:2010-07-23 08:36:53

标签: c++ c++11 tuples

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

例如,_push(5)应该在堆栈上推送'5'(stack of lua)并返回1(因为推送了一个值),而_push(std::make_tuple(5, "hello"))应该推'5'并且'你好'并返回2.

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

无论如何,我无法使其与元组一起使用:

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:我知道你可以使用模板结构来实现这一点(这实际上就是我之前所做的),但我想知道如何使用函数 < / p>

PS 2:PS2已解决,感谢GMan

6 个答案:

答案 0 :(得分:6)

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

以下应该允许您遍历调用函数的元组。它基于你的逻辑,只有一些小的改变。 (Nstd::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;
                    });

哦,好吧。

答案 1 :(得分:1)

我用一些黑客解决了这个问题。这是代码:

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;
}

如果找到更好的方法,请不要犹豫发布

答案 2 :(得分:0)

如果你想通过一个带有函数的元组进行迭代,你可以使用(litte)位样板来完成。我们的想法是构建一个对应于元组索引的可变整数列表,然后使用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)... );
}

这将扩展元组内容并将其提供给功能栏。

希望这会有所帮助:)

答案 3 :(得分:0)

这是我能想到的更简单的解决方案之一。我用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") );
}

答案 4 :(得分:0)

不是让一个函数做两件事,而是分开关注点:

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

然后问题根本不再存在_push!关于是否推送一个包含多个值的项目(我不熟悉Lua,但我知道它有一个主容器类)或者从一个序列推送多个项目,你没有含糊不清。

重命名这些功能可能会有所帮助:

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

为了进行比较,请考虑std :: vector如何始终对一个项目使用push_back(_append)并插入多个项目(_extend);它不会试图混合这两个概念。

答案 5 :(得分:-1)

为每个

编辑通用
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 << ']';