是否可能在运行时迭代mpl::vector而不实例化向量中的类型?

18
通常情况下,我会使用boost::mpl::for_each<>()来遍历boost::mpl::vector,但这需要一个具有如下所示模板函数声明的函数对象: template<typename T> void operator()(T&){T::staticCall();} 我的问题是,我不希望for_each<>实例化对象T。我根本不需要operator()中的T参数。是否有一种方法可以实现这个目标,或者是否有一种替代for_each<>的方法,该方法不会向模板函数传递类型为T的对象?
最理想的情况是,我希望operator()定义如下: template<typename T> void operator()(){T::staticCall();} 当然,在调用之前我不希望实例化T。如果还有其他提示/建议,也欢迎提供。
6个回答

14

我遇到了同样的情况并提供了不同的解决方案,我想分享一下。虽然它不是那么明显,但它使用了现有的算法。思路是使用指针。

typedef boost::mpl::vector<type1*, type2*> container;

struct functor
{
    template<typename T> void operator()(T*)
    {
        std::cout << "created " << typeid(T).name() << std::endl;
    }
};

int main()
{
    boost::mpl::for_each<container>(functor());
}

所以我们得到了一个空指针,但由于我们不会使用它们,所以我们并不在意。

正如我之前所说的,在代码中这并不明显,可能需要一些额外的注释,但仍然可以在不编写任何额外代码的情况下解决问题。

添加

我认为Diego Sevilla提出了类似的建议。


4
这很聪明。你可以使用mpl::transform来即时定义“指针”版本:boost :: mpl :: for_each <boost :: mpl :: transform <MyTypes,boost :: add_pointer <boost :: mpl :: _1> :: type >(Functor()) - kizzx2
4
你甚至可以不提供指针,而是提供一个包含该类型的空类型。 - Klaim

13

这是一个有趣的问题!据我所知,Boost.MPL似乎没有提供这样的算法。但是,使用迭代器编写自己的算法不应该太困难。

以下是可能的解决方案:

#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/next_prior.hpp>
#include <boost/mpl/vector.hpp>

using namespace boost::mpl;


namespace detail {

template < typename Begin, typename End, typename F >
struct static_for_each
{
    static void call( )
    {
        typedef typename Begin::type currentType;

        F::template call< currentType >();
        static_for_each< typename next< Begin >::type, End, F >::call();
    }
};


template < typename End, typename F >
struct static_for_each< End, End, F >
{
    static void call( )
    {
    }
};

} // namespace detail


template < typename Sequence, typename F >
void static_for_each( )
{
    typedef typename begin< Sequence >::type begin;
    typedef typename end< Sequence >::type   end;

    detail::static_for_each< begin, end, F >::call();
}

[命名可能不太恰当,但是...] 以下是如何使用该算法的步骤:
struct Foo
{
    static void staticMemberFunction( )
    {
        std::cout << "Foo";
    }
};


struct Bar
{
    static void staticMemberFunction( )
    {
        std::cout << "Bar";
    }
};


struct CallStaticMemberFunction
{
    template < typename T >
    static void call()
    {
        T::staticMemberFunction();
    }
};


int main()
{
    typedef vector< Foo, Bar > sequence;

    static_for_each< sequence, CallStaticMemberFunction >(); // prints "FooBar"
}

这里有非常有用的指导。谢谢。 - Marcin

1

这里有一个备选方案,受到了Luc Touraille's answer的启发。

这个版本使用元函数类而不是函数来完成,这使得static_for_each可以在函数作用域之外被调用(如果工作必须完全在编译时完成,这将非常有用,因为你不需要在运行时调用不必要的函数)。

此外,由于firstlast typedefs的存在,它提供了更多的交互性,允许在必要时从循环中获取信息,有点像函数的return的工作方式。

你还可以通过传递给元函数类F的第二个模板参数Previous来在每次迭代中访问前一次迭代的结果。

最后,你可以使用Initial模板参数向循环过程提供数据,它将作为第一次迭代的Previous参数的值。

# include <boost/mpl/begin_end.hpp>
# include <boost/mpl/next_prior.hpp>
# include <boost/mpl/apply.hpp>

namespace detail_static_for_each
{
  // Loop
  template<typename Begin, typename End, typename F, typename Previous>
  struct static_for_each
  {
  private:
    typedef typename Begin::type                                current_type;

  public:
    typedef typename boost::mpl::apply<F, current_type, Previous>::type             first;
    typedef typename static_for_each<typename boost::mpl::next<Begin>::type, End, F, first>::last   last;
  };

  // End of loop
  template<typename End, typename F, typename Last>
  struct static_for_each<End, End, F, Last>
  {
  public:
    typedef Last    first;
    typedef Last    last;
  };

} // namespace detail_static_for_each

// Public interface
template<typename Sequence, typename F, typename Initial = void>
struct  static_for_each
{
private:
  typedef typename boost::mpl::begin<Sequence>::type        begin;
  typedef typename boost::mpl::end<Sequence>::type          end;

  typedef typename detail_static_for_each::static_for_each<begin, end, F, Initial>  loop;

public:
  typedef typename  loop::first                 first;
  typedef typename  loop::last                  last;
};

这里有一个简单的例子,既可以提供数据,也可以检索数据:

# include <iostream>

# include <boost/type_traits/is_same.hpp>

# include <boost/mpl/if.hpp>
# include <boost/mpl/vector.hpp>

# include "static_for_each.hpp"

struct is_there_a_float                                                                                                                                                                                              
{                                                                                                                                                                                                                    
    template<typename currentItem, typename PreviousIterationType>                                                                                                                                                     
    struct apply                                                                                                                                                                                                       
    {                                                                                                                                                                                                                  
        typedef typename boost::mpl::if_< PreviousIterationType,                                                                                                                                                         
                                          PreviousIterationType,                                                                                                                                                         
                                          boost::is_same<float, currentItem> >::type    type;                                                                                                                        
    };                                                                                                                                                                                                                 
};

struct  test                                                                                                                                                                                                         
{                                                                                                                                                                                                                    
    typedef boost::mpl::vector< char, long, long, double, float, int, char > sequence;                                                                                                                                 

    typedef static_for_each<sequence, is_there_a_float, boost::false_type>::last    found;                                                                                                               
};

int     main(void)                                                                                                                                                                                                   
{                                                                                                                                                                                                                    
    std::cout << std::boolalpha << test::found::value << std::endl;                                                                                                                                                    

    return (0);                                                                                                                                                                                                        
}

这些特性使得使用static_for_each更类似于使用常见的运行时循环(whileforBOOST_FOREACH ...),因为您可以更直接地与循环交互。


据我所知,您只是重新实现了 mpl::foldmpl::for_each 的主要特点是它在运行时操作(虽然迭代是在编译时执行的,但它调用运行时操作),我假设OP希望这种行为。否则,他将使用MPL提供的众多编译时算法之一。 - Luc Touraille
@LucTouraille 我同意你的观点。这个回答更多是作为额外信息而不是直接回答。 - Drax

1
首先,你代码中的静态调用意味着你的对象将存在。在这方面,之前或之后都没有意义。唯一有意义的时候是当T是一个模板时,才会出现“我不想在调用之前实例化T”的情况。但它不是,因为它不能是。确实,正是那一行代码导致了对象的存在,但我很确定一旦产品被编译,它不会只存在于那里。
其次,我不认为目前有一种方法可以使用for_each而不实例化。在我看来,这是MPL中的一个错误,由于使用operator()而引起的。我不会说这是错的,因为我认识这位开发者,他比我聪明多了,但现在你提出这个问题,从这里看来似乎是这样。
所以,我认为你必须重新制作一个for_each,调用一个不需要参数的模板函数。我几乎可以肯定这是可能的,但同样确定它不是MPL中预制组件的一部分。

1
Marcin,你说得非常正确。我一直在思考这个问题,但是我没有找到一个简单的解决方案。即使你无法编写空的operator(),也可以使用指针来代替,因为它不需要实际对象存在。看来你需要自己实现一个方案。

我认为没有“简单”的解决方案,因为“简单”的解决方案已经存在:使用mpl::beginmpl::end。基本上你需要自己遍历它。就像你必须写非常长的for(std::vector<int>::iterator it = v.begin() ... 迭代器的东西一样。你可以编写一个高阶函数来简化它,但最终你仍然需要像@Luc Touraille建议的那样遍历它。 - kizzx2

0

我喜欢(点赞)使用指针和自定义的*_for_each函数的解决方案。如果目标是避免在需要之前创建对象,则可以使用类型包装器T的替代方法。

template<typename T>
struct Wrapper
{
  typedef T type;
};

struct Functor
{
  template<typename T> void operator()(T t)
  {
    T::type obj(1);
    T::type::static_fuc();
  }
};

struct T1
{
  T1(int a) : m_a(a) { }
  int m_a;
  static inline void static_fuc() { }
};
struct T2
{
  T2(int a) : m_a(a) { }
  int m_a;
  static inline void static_fuc() { }
};

void fun()
{
  namespace mpl=boost::mpl;
  typedef mpl::vector<Wrapper<T1>,Wrapper<T2> > t_vec;
  mpl::for_each<t_vec>(Functor());
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接