类型敏感元组访问者 [英] Type sensitive tuple visitor

查看:58
本文介绍了类型敏感元组访问者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个 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 确定要调用哪个函数。不幸的是,这行不通/不是一个好的解决方案:


  1. 编译器抱怨递归实例化

  2. const T& dummy 不是一个干净的解决方案

我想知道 enable_if 是做出决定的正确策略,并且如何通过 std :: tuple 递归地迭代来捕获第一种类型并将所有其余参数保持在生命状态。通读如何拆分元组?,但它没有任何决定。 / p>

如何在C ++ 11中以正确且可移植的方式实现这样的事情?

解决方案

好吧,这比我想象的要难,但是有效。



某些地方您做错了/我修改了


  1. 您无法对此进行评估: std :: is_function<类型名称T :: tip> :: value ,因为 T :: tip 不是类型。即使可以对此进行评估,当 T :: tip 不存在时会发生什么?替换仍然会失败。

  2. 由于您将const引用用作元组的内部类型,因此必须先清理它们,然后才能在其中查找tip成员。清理是指删除const并删除引用。

  3. 该虚拟类型的东西不是一个好主意,不需要使用该参数。您可以使用 std :: tuple_element 实现相同的操作,该方法从元组中检索第i个类型。

  4. 我修改了 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:

  1. The compiler complains about recursive instantiation
  2. 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:

  1. You can't evaluate this: std::is_function< typename T::tip >::value, since T::tip is not a type. Even if this could be evaluated, what would happen when T::tip does not exist? Substitution would still fail.
  2. 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.
  3. 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.
  4. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆