typedef std::tuple< int, double > Tuple;
Tuple t;
int a = std::get<0>(t);
double b = std::get<1>(t);
for( size_t i = 0; i < std::tuple_size<Tuple>::value; i++ ) {
std::tuple_element<i,Tuple>::type v = std::get<i>(t);// will not compile because i must be known at compile time
}
我知道可以为get std::get
编写代码(例如参见iterate over tuple),是否可以让std::tuple_element
工作?
一些限制(可以放宽):
没有可变参数模板,没有Boost
答案 0 :(得分:7)
C ++是一种编译时键入的语言。您不能拥有C ++编译器在编译时无法确定的类型。
您可以使用各种形式的多态来解决这个问题。但是在一天结束时,每个变量都必须具有明确定义的类型。因此,虽然您可以使用Boost.Fusion算法迭代元组中的变量,但是您不能有一个循环,其中循环的每次执行可能使用与最后一个不同的类型。
Boost.Fusion可以逃脱它的唯一原因是因为它不使用循环。它使用模板递归来“迭代”每个元素并调用用户提供的函数。
答案 1 :(得分:4)
如果你想不加强,iterate over tuple的答案已经告诉你需要知道的一切。您必须编写一个编译时for_each
循环(未经测试)。
template<class Tuple, class Func, size_t i>
void foreach(Tuple& t, Func fn) {
// i is defined at compile-time, so you can write:
std::tuple_element<i, Tuple> te = std::get<i>(t);
fn(te);
foreach<i-1>(t, fn);
}
template<class Tuple, class Func>
void foreach<0>(Tuple& t, Func fn) { // template specialization
fn(std::get<0>(t)); // no further recursion
}
并使用它:
struct SomeFunctionObject {
void operator()( int i ) const {}
void operator()( double f ) const {}
};
foreach<std::tuple_size<Tuple>::value>(t, SomeFunctionObject());
但是,如果你想迭代一个元组的成员,Boost.Fusion真的是要走的路。
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/adapted/boost_tuple.hpp>
并在你的代码中写:
boost::for_each(t, SomeFunctionObject());
这是boost :: tuple的一个例子。 boost :: fusion有一个适配器可以在这里使用std :: tuple:http://groups.google.com/group/boost-list/browse_thread/thread/77622e41af1366af/
答案 2 :(得分:1)
不,这不可能像你描述的那样。基本上,您必须为i
的每个可能的运行时值编写代码,然后使用一些调度逻辑(例如switch(i)
)来根据实际的运行时值运行正确的代码。 i
。
在实践中,可能可以使用模板为i
的不同值生成代码,但我不确定如何执行此操作,以及它是否实用。你所描述的听起来像一个有缺陷的设计。
答案 3 :(得分:1)
这是我的元组foreach / transformation函数:
#include <cstddef>
#include <tuple>
#include <type_traits>
template<size_t N>
struct tuple_foreach_impl {
template<typename T, typename C>
static inline auto call(T&& t, C&& c)
-> decltype(::std::tuple_cat(
tuple_foreach_impl<N-1>::call(
::std::forward<T>(t), ::std::forward<C>(c)
),
::std::make_tuple(c(::std::get<N-1>(::std::forward<T>(t))))
))
{
return ::std::tuple_cat(
tuple_foreach_impl<N-1>::call(
::std::forward<T>(t), ::std::forward<C>(c)
),
::std::make_tuple(c(::std::get<N-1>(::std::forward<T>(t))))
);
}
};
template<>
struct tuple_foreach_impl<0> {
template<typename T, typename C>
static inline ::std::tuple<> call(T&&, C&&) { return ::std::tuple<>(); }
};
template<typename T, typename C>
auto tuple_foreach(T&& t, C&& c)
-> decltype(tuple_foreach_impl<
::std::tuple_size<typename ::std::decay<T>::type
>::value>::call(std::forward<T>(t), ::std::forward<C>(c)))
{
return tuple_foreach_impl<
::std::tuple_size<typename ::std::decay<T>::type>::value
>::call(::std::forward<T>(t), ::std::forward<C>(c));
}
示例用法使用以下实用程序来允许将元组打印到ostream:
#include <cstddef>
#include <ostream>
#include <tuple>
#include <type_traits>
template<size_t N>
struct tuple_print_impl {
template<typename S, typename T>
static inline void print(S& s, T&& t) {
tuple_print_impl<N-1>::print(s, ::std::forward<T>(t));
if (N > 1) { s << ',' << ' '; }
s << ::std::get<N-1>(::std::forward<T>(t));
}
};
template<>
struct tuple_print_impl<0> {
template<typename S, typename T>
static inline void print(S&, T&&) {}
};
template<typename S, typename T>
void tuple_print(S& s, T&& t) {
s << '(';
tuple_print_impl<
::std::tuple_size<typename ::std::decay<T>::type>::value
>::print(s, ::std::forward<T>(t));
s << ')';
}
template<typename C, typename... T>
::std::basic_ostream<C>& operator<<(
::std::basic_ostream<C>& s, ::std::tuple<T...> const& t
) {
tuple_print(s, t);
return s;
}
最后,这是示例用法:
#include <iostream>
using namespace std;
struct inc {
template<typename T>
T operator()(T const& val) { return val+1; }
};
int main() {
// will print out "(7, 4.2, z)"
cout << tuple_foreach(make_tuple(6, 3.2, 'y'), inc()) << endl;
return 0;
}
请注意,构造可调用对象,以便在需要时可以保持状态。例如,您可以使用以下命令查找元组中可以动态转换为T的最后一个对象:
template<typename T>
struct find_by_type {
find() : result(nullptr) {}
T* result;
template<typename U>
bool operator()(U& val) {
auto tmp = dynamic_cast<T*>(&val);
auto ret = tmp != nullptr;
if (ret) { result = tmp; }
return ret;
}
};
请注意,这样做的一个缺点是它要求callable返回一个值。但是,重写它以检测返回类型对于给定输入类型是否为空,然后跳过结果元组的元素并不困难。更简单的是,您可以完全删除返回值聚合内容,只需将foreach调用用作元组修饰符。
编辑: 我刚刚意识到使用foreach函数可以简单地编写元组写入器(我的元组打印代码比foreach代码长得多)。
template<typename T>
struct tuple_print {
print(T& s) : _first(true), _s(&s) {}
template<typename U>
bool operator()(U const& val) {
if (_first) { _first = false; } else { (*_s) << ',' << ' '; }
(*_s) << val;
return false;
}
private:
bool _first;
T* _s;
};
template<typename C, typename... T>
::std::basic_ostream<C> & operator<<(
::std::basic_ostream<C>& s, ::std::tuple<T...> const& t
) {
s << '(';
tuple_foreach(t, tuple_print< ::std::basic_ostream<C>>(s));
s << ')';
return s;
}