是否可以在运行时迭代 mpl::vector 而不实例化向量中的类型?
Posted
技术标签:
【中文标题】是否可以在运行时迭代 mpl::vector 而不实例化向量中的类型?【英文标题】:Is it possible to iterate an mpl::vector at run time without instantiating the types in the vector? 【发布时间】:2010-10-29 15:38:07 【问题描述】:一般来说,我会使用boost::mpl::for_each<>()
来遍历boost::mpl::vector
,但这需要一个仿函数,其模板函数声明如下:
template<typename T> void operator()(T&)T::staticCall();
我的问题是我不希望对象 T 被 for_each<>
实例化。我根本不需要operator()
中的 T 参数。有没有办法做到这一点,或者for_each<>
的替代方法不会将 T 类型的对象传递给模板函数?
理想情况下,我希望 operator() 定义如下所示:
template<typename T> void operator()()T::staticCall();
当然,我不希望 T 在调用之前被实例化。也欢迎任何其他提示/建议。
【问题讨论】:
【参考方案1】:刚刚遇到同样的情况,针对问题提供了不同的解决方案,我想分享一下。它并不那么明显,但它使用了现有的算法。这个想法是改用指针。
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());
所以这里我们得到一个空指针,但我们不在乎,因为我们不会使用它们。
正如我之前所说,这在代码中并不明显,可能需要一些额外的 cmets,但它仍然可以解决问题,而无需编写任何额外的代码。
添加
我认为 Diego Sevilla 提出了类似的建议。
【讨论】:
这很聪明。您可以使用mpl::transform
动态定义“指针”版本:boost::mpl::for_each<boost::mpl::transform<MyTypes, boost::add_pointer<boost::mpl::_1> >::type>(Functor());
@kizzx2 你甚至可以不提供指针,而是提供一个包含该类型的空类型。【参考方案2】:
有趣的问题!据我所知,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"
【讨论】:
这里有非常有用的指导。谢谢。【参考方案3】:首先,代码中的静态调用意味着您的对象将存在。在这方面,之前/之后是没有意义的。唯一一次,“我不希望 T 在调用之前被实例化”是有意义的,当 T 是一个模板时。不是,因为不可能。确实是那一行导致对象存在,但我很确定一旦产品被编译,它就不会只存在于那里。
其次,我不相信有一种当前的方法可以在不实例化的情况下使用 for_each。恕我直言,这是 MPL 中的一个错误,由使用 operator() 的可疑决定引起。不会说这是错误的,因为我认识开发人员,而且他比我聪明得多,但是现在你提出这个问题似乎是这样。
所以,我认为您不得不重新制作一个调用不需要参数的模板化函数的 for_each。我几乎可以肯定它是可能的,但也同样可以肯定它在 MPL 中作为预制组件并不容易获得。
【讨论】:
【参考方案4】:Marcin,你说的很对。我一直在考虑这个问题,但我没有看到一个简单的解决方案。即使你不能写空的@987654321@,至少可以使用一个不需要实际对象存在的指针。看来你必须推出自己的实现。
【讨论】:
我认为没有“简单”的解决方案,因为 easy 解决方案已经存在:使用mpl::begin
和mpl::end
。基本上你需要自己遍历它。就像你必须为迭代器写很长的for(std::vector<int>::iterator it = v.begin() ...
东西一样。您可以编写一个高阶函数来简化它,但最终您仍然需要像@Luc Touraille 建议的那样遍历它。【参考方案5】:
这是一个受Luc Touraille's answer启发的替代解决方案。
这个版本是使用Metafunction classes而不是函数完成的.
此外,由于first
和last
类型定义,它提供了更多交互,允许在必要时从循环中获取信息,有点像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
的使用更类似于使用常见的运行时循环(while
、for
、BOOST_FOREACH ...),因为您可以更直接地与循环交互。
【讨论】:
据我所知,您只是重新实现了mpl::fold。mpl::for_each
的主要特点是它在运行时运行(好吧,迭代是在编译时执行的,但它调用运行时操作),我假设 OP 想要这种行为。否则,他会使用 MPL 提供的众多编译时算法之一。
@LucTouraille 我同意你的看法。这个答案更多的是作为奖励信息而不是直接答案。【参考方案6】:
我喜欢(赞成)带有指针和自己定义的 *_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());
【讨论】:
以上是关于是否可以在运行时迭代 mpl::vector 而不实例化向量中的类型?的主要内容,如果未能解决你的问题,请参考以下文章
如何在运行时检查权限而不抛出 SecurityException?
Python:如何在迭代列表时从列表中删除元素而不跳过未来的迭代