我有一个名为_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
答案 0 :(得分:6)
我没有编译器来测试任何这些,所以你必须报告任何问题。
以下应该允许您遍历调用函数的元组。它基于你的逻辑,只有一些小的改变。 (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;
});
哦,好吧。
答案 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 << ']';