将实例数组实现到不同类型的好方法是什么

时间:2013-04-13 00:28:30

标签: c++ c++11

我正在编写一个大量使用模板的数学库,特别是可变参数模板,并希望实现一个Sum函数,它可以使用不同类型的任意数量的函子并存储它们。我还想避免任何动态内存分配(作为对我自己的练习,我一般都没有动态内存分配)。

我找不到任何帮助的问题是如何将类实例存储到不同类型。类似的东西:

    any_array<Types...> a = {Type1(), Type2(), Type3(), ...};

以某种方式迭代a获取每个值的正确类型。使用boost不会成为问题因为我已经在其他地方使用了它。

我提出了一个似乎运作良好的解决方案,但我想看看有什么方法可以解决这个问题。

我对此的解决方案是一个基本上看起来的类(完全编译的实现和示例可以在下面找到):

    template <class ... Functions>
    class Sum
    {
        char functions[num_bytes<Functions...>::value];

        template <class Next, class ... Others>
        void SetFunctions(int offset, Next f, Others ... others)
        {
            Next * p = (Next*)(functions + offset);
            *p = f;
            SetFunctions(offset + sizeof(Next), others...);
        }

        template <class Last>
        void SetFunctions(int offset, Last f)
        {
            Last * p = (Last*)(functions + offset);
            *p = f;
        }
    public:
        Sum(Functions ... funcs)
        {
             SetFunctions(0, funcs...);
        }
    };

我喜欢这个解决方案,因为它可以很容易地推广到我想要的任何类型的累积函数,并且下面的实现对用户来说是隐藏的。我不确定是否将原始字节存储到这些对象中,但不能认为这本身有什么问题。这种概括的可能性使我怀疑它已经在某处实现但我在自己的搜索中找不到任何东西。

FWIW这是我实施的完整示例:

    #include <functional>
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/sizeof.hpp>
    #include <boost/mpl/accumulate.hpp>
    #include <boost/mpl/plus.hpp>
    #include <boost/mpl/placeholders.hpp>
    #include <boost/mpl/int.hpp>
    #include <iostream>
    #include <boost/utility/enable_if.hpp>

    using namespace boost::mpl::placeholders;
    using namespace boost::mpl;

    //Returns the sum of the number of bytes each class takes up.
    //This is used as the size of the array we need to create.
    template <class ... Args>
    struct num_bytes : 
        boost::mpl::accumulate<vector<Args...>,
                               int_<0>,
                               plus<_1, sizeof_<_2> > >::type
    {
    };

    template <class ... Args>
    struct empty_list
    {
        typedef empty_list type;
        static const bool value = sizeof...(Args) == 0;
    };

    template <class ... Functions>
    class Sum
    {
    public:
        Sum(Functions ... functions)
        {
            SetFunctions(0, functions...);
        }

        inline double operator()(double x)
        {
            return evaluate<Functions...>(0, x);
        }
    private:

        template <class Next, class ... Others>
        inline void SetFunctions(int offset, Next f, Others ... funcs)
        {
            Next * p = (Next*)(functions + offset);
            *p = f;
            SetFunctions(offset + sizeof(Next), funcs...);
        }

        template <class Last>
        inline void SetFunctions(int offset, Last f)
        {
            Last * p = (Last*)(functions + offset);
            *p = f;
        }

        //Because we are not passing our function objects down, we
        //have to manually disable this function overload to end the recursive
        //instantiations of this function.
        template <class Next, class ... Others>
        inline double evaluate(int offset, double x, 
                               typename boost::enable_if_c<!empty_list<Others...>::value>::type * dummy = NULL)
        {
            Next * p = (Next*)(functions + offset);
            return evaluate<Others...>(offset + sizeof(Next), x) + (*p)(x);
        }

        template <class Last>
        inline double evaluate(int offset, double x)
        {
            Last * p = (Last*)(functions+offset);
            return (*p)(x);
        }

        char functions[num_bytes<Functions...>::value];
    };

    //Function to help initialize a Sum object
    template <class ... Functions>
    Sum<Functions...> GetSum(Functions ... functions)
    {
        return Sum<Functions...>(functions...);
    }

    //return function object of the form f(x) = x + n.
    std::binder2nd<std::plus<int> > GetTestFunction(int n)
    {
        return std::bind2nd(std::plus<int>(), n);
    }

    int main()
    {
        auto sum = GetSum(GetTestFunction(0),
                          GetTestFunction(1),
                          GetTestFunction(2));
        std::cout << sum(0) << ' ' << sum(1) << std::endl;
        return 0;
    }

运行时输出3 6

注意:我无法使用gcc-4.6进行编译,只使用gcc-4.7并使用命令行:         g ++ - 4.7 -std = c ++ 0x test_sum.cpp -Wall

2 个答案:

答案 0 :(得分:3)

在我看来,你需要的是std::tuple

template <class ... Functions>
class Sum
{
    std::tuple<Functions...> functions;

public:
    Sum(Functions&& ... funcs)
      : functions( std::forward< Functions >( funcs )... )
    {
    }
};

答案 1 :(得分:1)

我不知道这是不是一个好方法,但这是一种方式:

#define BOOST_RESULT_OF_USE_DECLTYPE
#include <functional>
#include <iostream>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/fold.hpp>
#include <boost/phoenix.hpp>

namespace fusion=boost::fusion;
namespace phx=boost::phoenix;


template <class ... Functions>
class Sum
{
public:
    Sum(Functions ... functions):functions_(functions...)
    {

    }

    inline double operator()(double x)
    {
        return fusion::fold(functions_,0,eval(x));
    }
private:

    template <typename Arg>
    struct evaluate
    {
        evaluate(const Arg& arg):arg_(arg){}
        template <typename State, typename Func>
        State operator()(State const& current_state, const Func& f)
        {
           return current_state + f(arg_);
        }
        Arg arg_;
    };

    template <typename Arg>
    evaluate<Arg> eval(const Arg& arg)
    {
       return evaluate<Arg>(arg);
    }


    fusion::vector<Functions...> functions_;
};

//Function to help initialize a Sum object
template <class ... Functions>
Sum<Functions...> GetSum(Functions ... functions)
{
    return Sum<Functions...>(functions...);
}

//return function object of the form f(x) = x + n.
std::binder2nd<std::plus<int> > GetTestFunction(int n)
{
    return std::bind2nd(std::plus<int>(), n);
}

struct plus_two
{
   double operator()(double arg) const
   {
      return arg+2;
   }
};

int main()
{
    auto sum = GetSum(GetTestFunction(0),
                      phx::arg_names::_1+1,
                      plus_two());
    std::cout << sum(0) << ' ' << sum(1) << std::endl;
    return 0;
}