类型敏感元组访问者 [英] Type sensitive tuple visitor
问题描述
假设我有一个 std :: tuple
由类似类型的
struct A {
static void tip();
};
结构B {
static void tip();
};
结构Z {
};
std :: tuple< const A&,const B&,const Z& tpl;
是,我需要单独的 A
, B
。 ( :: tip()
的实现因每种类型而异。)我尝试实现的是类型敏感的访问者,该访问者从头开始遍历元组到最后。在访问 T
类型的特定元素后,应调用函数,具体取决于 T
是否具有 :: tip()
方法。在上面的简单示例中,只有 A
和 B
具有 :: tip()
已实施,而 Z
未实施。因此,迭代器应使用 :: tip()
方法为类型调用两次函数,并为其他函数调用一次。
这是我想出的:
template< int N,bool end>
结构TupleIter
{
template<类型名T,类型名... Ts>
类型名称std :: enable_if< std :: is_function<类型名T :: tip> :: value,void> :: type
静态Iter(const T& dummy,const std :: tuple< Ts ...>&tpl){
std :: cout<< tip\n;
std :: get< N>(tpl); //完成
TupleIter< N + 1,sizeof ...(Ts)== N + 1> :: Iter(std :: get< N + 1>(tpl),tpl);
}
template<类型名T,类型名... Ts>
类型名称std :: enable_if< ! std :: is_function<类型名T :: tip> :: value,void> :: type
静态Iter(const T& dummy,const std :: tuple< Ts ...>&tpl){
std :: cout<< 没有小费;
std :: get< N>(tpl); //完成
TupleIter< N + 1,sizeof ...(Ts)== N + 1> :: Iter(std :: get< N + 1>(tpl),tpl);
}
};
模板<整数N
结构TupleIter< N,true>
{
template<类型名T,类型名... Ts>
static void Iter(const std :: tuple< Ts ...& tpl){
std :: cout<< end\n;
}
};
我使用类型为 dummy
的实例元素在迭代器位置的位置,并通过 enable_if
确定要调用哪个函数。不幸的是,这行不通/不是一个好的解决方案:
- 编译器抱怨递归实例化
-
const T& dummy
不是一个干净的解决方案
我想知道 enable_if
是做出决定的正确策略,并且如何通过 std :: tuple
递归地迭代来捕获第一种类型并将所有其余参数保持在生命状态。通读如何拆分元组?,但它没有任何决定。 / p>
如何在C ++ 11中以正确且可移植的方式实现这样的事情?
好吧,这比我想象的要难,但是这有效。
某些地方您做错了/我修改了
- 您无法对此进行评估:
std :: is_function<类型名称T :: tip> :: value
,因为T :: tip
不是类型。即使可以对此进行评估,当T :: tip
不存在时会发生什么?替换仍然会失败。 - 由于您将const引用用作元组的内部类型,因此必须先清理它们,然后才能在其中查找tip成员。清理是指删除const并删除引用。
- 该虚拟类型的东西不是一个好主意,不需要使用该参数。您可以使用
std :: tuple_element
实现相同的操作,该方法从元组中检索第i个类型。 - 我修改了
TupleIter
的模板参数如下:
TupleIter
处理大小为n的元组中的第index个类型。
template< size_t索引,size_t n>
结构TupleIter;
整个代码是这样的:
#include< tuple>
#include< iostream>
#include< type_traits>
结构A {
static void tip();
};
结构B {
static void tip();
};
结构Z {
};
//指示模板参数是否包含名为tip的静态成员。
template< class T>
struct has_tip {
template< class U>
静态字符测试(decltype(& U :: tip));
template< class U>
静态浮点测试(...);
静态常量值= sizeof(test< typename std :: decay< T> :: type>(0))== sizeof(char);
};
//指示第n个类型是否包含提示静态成员
template< size_t n,typename ... Ts>
struct nth_type_has_tip {
static const bool value = has_tip< typename std :: tuple_element< n,std :: tuple< Ts ...>> :: type> :: value;
};
//通用迭代
template< size_t索引,size_t n>
结构TupleIter
{
template<类型名称... Ts>
类型名称std :: enable_if< nth_type_has_tip< index,Ts ...> :: value,void> :: type
静态Iter(const std :: tuple< Ts ...>&tpl)
{
std :: cout<< tip\n;
TupleIter< index + 1,n> :: Iter(tpl);
}
template<类型名称... Ts>
类型名称std :: enable_if< !nth_type_has_tip< index,Ts ...> :: value,void> :: type
静态Iter(const std :: tuple< Ts ...>&tpl){
std :: cout<< 没有小费;
TupleIter< index + 1,n> :: Iter(tpl);
}
};
//基础类,我们已经到达元组末尾
template< size_t n>
结构TupleIter< n,n>
{
template< typename ... Ts>
static void Iter(const std :: tuple< Ts ...& tpl){
std :: cout<< end\n;
}
};
//将首次调用转发给TupleIter<> :: Iter
template< typename ... Ts>的Helper函数
void iterate(const std :: tuple< Ts ...> tup){
TupleIter< 0,sizeof ...(Ts)> :: Iter(tup);
}
int main(){
A a;
B b;
Z z;
std :: tuple< const A&,const B&,const Z& tup(a,b,z);
iterate(tup);
}
Suppose I have a std::tuple
made up of types like
struct A {
static void tip();
};
struct B {
static void tip();
};
struct Z {
};
std::tuple<const A&,const B&,const Z&> tpl;
Yes, I need separate A
, B
. (The implementation of ::tip()
differs for each type.) What I try to implement is a type-sensitive "visitor" that iterates through the tuple starting from the beginning to the end. Upon visiting a particular element of type T
a function should be called depending on whether T
has the ::tip()
method or not. In the simple example of above only A
and B
have ::tip()
implemented and Z
not. So, the iterator should call twice the function for types with the ::tip()
method and once the other function.
Here is what I came up with:
template< int N , bool end >
struct TupleIter
{
template< typename T , typename... Ts >
typename std::enable_if< std::is_function< typename T::tip >::value , void >::type
static Iter( const T& dummy , const std::tuple<Ts...>& tpl ) {
std::cout << "tip\n";
std::get<N>(tpl); // do the work
TupleIter<N+1,sizeof...(Ts) == N+1>::Iter( std::get<N+1>(tpl) , tpl );
}
template< typename T , typename... Ts >
typename std::enable_if< ! std::is_function< typename T::tip >::value , void >::type
static Iter( const T& dummy , const std::tuple<Ts...>& tpl ) {
std::cout << "no tip\n";
std::get<N>(tpl); // do the work
TupleIter<N+1,sizeof...(Ts) == N+1>::Iter( std::get<N+1>(tpl) , tpl );
}
};
template< int N >
struct TupleIter<N,true>
{
template< typename T , typename... Ts >
static void Iter( const std::tuple<Ts...>& tpl ) {
std::cout << "end\n";
}
};
I use a dummy
instance of the type of the element at the iterator position and decide via enable_if
which function to call. Unfortunately this doesn't work/isn't a nice solution:
- The compiler complains about recursive instantiation
- The
const T& dummy
is not a clean solution
I was wondering if enable_if
is the right strategy to do the decision and how can one recursively iterate through the std::tuple
capturing the first type and keeping all the remaining arguments in vital state. Read through How to split a tuple? but it doesn't do any decision.
How can one implement such a thing in a correct and portable way in C++11?
Well, it was harder than I expected, but this works.
Some things you were doing wrong/that I modified:
- You can't evaluate this:
std::is_function< typename T::tip >::value
, sinceT::tip
is not a type. Even if this could be evaluated, what would happen whenT::tip
does not exist? Substitution would still fail. - Since you use const references as your tuple's inner types, you had to clean them before trying to find the tip member inside them. By cleaning I mean removing const and removing the reference.
- That dummy type stuff was not a good idea, there was no need to use that parameter. You can achieve the same thing using
std::tuple_element
, which retrieves the i-th type from a tuple. - I modified
TupleIter
's template parameters to the following, which means:
"TupleIter
that processes the index-th type, inside a tuple of size n".
template<size_t index, size_t n>
struct TupleIter;
The whole code is this:
#include <tuple>
#include <iostream>
#include <type_traits>
struct A {
static void tip();
};
struct B {
static void tip();
};
struct Z {
};
// Indicates whether the template parameter contains a static member named tip.
template<class T>
struct has_tip {
template<class U>
static char test(decltype(&U::tip));
template<class U>
static float test(...);
static const bool value = sizeof(test<typename std::decay<T>::type>(0)) == sizeof(char);
};
// Indicates whether the n-th type contains a tip static member
template<size_t n, typename... Ts>
struct nth_type_has_tip {
static const bool value = has_tip<typename std::tuple_element<n, std::tuple<Ts...>>::type>::value;
};
// Generic iteration
template<size_t index, size_t n>
struct TupleIter
{
template< typename... Ts >
typename std::enable_if< nth_type_has_tip<index, Ts...>::value , void >::type
static Iter(const std::tuple<Ts...>& tpl)
{
std::cout << "tip\n";
TupleIter<index + 1, n>::Iter(tpl );
}
template< typename... Ts >
typename std::enable_if< !nth_type_has_tip<index, Ts...>::value , void >::type
static Iter(const std::tuple<Ts...>& tpl) {
std::cout << "no tip\n";
TupleIter<index + 1, n>::Iter(tpl );
}
};
// Base class, we've reached the tuple end
template<size_t n>
struct TupleIter<n, n>
{
template<typename... Ts >
static void Iter( const std::tuple<Ts...>& tpl ) {
std::cout << "end\n";
}
};
// Helper function that forwards the first call to TupleIter<>::Iter
template<typename... Ts>
void iterate(const std::tuple<Ts...> &tup) {
TupleIter<0, sizeof...(Ts)>::Iter(tup);
}
int main() {
A a;
B b;
Z z;
std::tuple<const A&,const B&,const Z&> tup(a,b,z);
iterate(tup);
}
这篇关于类型敏感元组访问者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!