非空函数模板递归元组和向量

时间:2015-03-12 12:24:26

标签: templates c++11 stdvector stdtuple

我已经创建了一个计算数字正弦的函数。如果是std::is_floating_point,则返回输入类型。但对于std::is_integral,它会返回double

template<class T , typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
double mysin(const T& t) // note, function signature is unmodified
{
    double a = t;
    return std::sin(a);
}

template<class T , typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr >
T mysin(const T& t) // note, function signature is unmodified
{
    return std::sin(t);
}

够容易。现在我希望这适用于vector s(或数组)和tuple s(或群集)。那样:

(pseudo code:)
std::vector<std::double> a = mysin(std::vector<std::int>); 
std::tuple<std::double, std::float> b = mysin(std::tuple<std::int, std::float>);
std::vector<std::tuple<std::double, std::float>> c = mysin(std::vector<std::tuple<std::int, std::float>>);
std::tuple<std::vector<std::double>, std::float> d = mysin(std::tuple<std::vector<std::int>, std::float>);
std::tuple<std::tuple<std::double, std::vector<std::double>>, std::float>> e = mysin(std::tuple<std::tuple<std::int, std::vector<std::int>>, std::float>>);
and so on...

在大多数关于tuple模板的示例中,函数要么没有返回值,要么返回累计值,要么具有与输入相同的返回类型。

我已经对这些主题进行了很多实验(其中包括): Traversing nested C++11 tuplec++11: building a std::tuple from a template functionHow to make a function that zips two tuples in C++11 (STL)?

最后一个特别有用。我的这个适用于tuple,但不适用于递归tuplestuple中的tuple s。)

最终(如果可以的话),我必须制作mycosmytanmyasin等。使用gcc 4.9.2。

**编辑:**所以这是我在Yakk的建议之后提出来的,并稍微调整一下:

#include <utility>
#include <vector>
#include <memory>
#include <typeinfo> // used for typeid
#include <tuple>
#include <cstdlib> // for math functions?
#include <cmath> // for math functions
#include <type_traits> // for std::enable_if

template<class T , typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
double mysin(const T& t) { // note, function signature is unmodified
    double a = t;
    return std::sin(a);
// printing a debug string here will
// print tuple elements reversed!!
}


template<class T , typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr >
T mysin(const T& t) {// note, function signature is unmodified
// printing a debug string here will
// print tuple elements reversed!!
    return std::sin(t);
}

struct sine_t {
    template<class T>
    auto operator()(T&&t)const->
        decltype(mysin(std::declval<T>())) {
            return mysin(std::forward<T>(t));
            }
};

template<class F>
struct vectorize {
    template<class T,
        class R=std::vector< std::result_of_t< vectorize<F>(T const&) > >
    >
        R operator()( std::vector<T> const& v ) const {
            R ret;
            ret.reserve(v.size());
            for( auto const& e : v ) {
                ret.push_back( vectorize<F>{}(e) );
                }
        return ret;
        }

    template<
        class X,
        class R=std::result_of_t< F(X const&) >
    >
        R operator()( X const& x ) const {
            return F{}(x);
            }   

    template<
        class R, 
        class... Ts, 
        size_t... Is
    >
    R tup_help( std::index_sequence<Is...>, std::tuple<Ts...> const& t ) const {
        return std::make_tuple( vectorize<F>{}(std::get<Is>(t))... );
        }

    template<
        class... Ts,
        class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
    >
    R operator()( std::tuple<Ts...> const& t ) const {
        return tup_help<R>( std::index_sequence_for<Ts...>{}, t );
        }

    };

//+++++++++++++++++++++++++++++++++++++++++

int main() {
    std::vector<int> a = {1 ,2};
    std::tuple<int, double, int, double> b (42, -3.14, 42, -3.14);

    auto c = vectorize<sine_t>()(a);
    auto d = vectorize<sine_t>()(b);

    std::vector<std::tuple<int, int> > e {std::make_tuple(1 ,2)};
    //This does not not work:
    //auto f = vectorize<sine_t>()(e);

    //This works:
    std::tuple<std::vector<int> > g ( a );
    auto f = vectorize<sine_t>()(g);

    return 0;
}

这很有效。需要c ++ 14。

1 个答案:

答案 0 :(得分:1)

首先是一个重载集对象。这很有用,因为它允许我们将整个重载集作为单个对象传递:

struct sine_t {
  template<class T>
  auto operator()(T&&t)const->
  decltype(mysin(std::declval<T>()))
  { return mysin(std::forward<T>(t)); }
};

接下来,我们想要“矢量化”给定的函数对象。

我们将从简单开始:

template<class F>
struct vectorize {
  template<class T, class R=std::vector< std::result_of_t< F(T const&) > >>
  R operator()( std::vector<T> const& v ) const {
    R ret;
    ret.reserve(v.size());
    for( auto const& e : v ) {
      ret.push_back( F{}(e) );
    }
    return ret;
  }
  template<class X, class R=std::result_of_t< F(X const&) >>
  R operator()( X const& x ) const {
    return F{}(x);
  }
};

这支持1级递归,并且只支持std::vector

为了允许嵌套std::vector的无限递归,我们修改operator()的{​​{1}}重载:

std::vector

现在我们支持 template< class T, class R=std::vector< std::result_of_t< vectorize<F>(T const&) > > > R operator()( std::vector<T> const& v ) const { R ret; ret.reserve(v.size()); for( auto const& e : v ) { ret.push_back( vectorize<F>{}(e) ); } return ret; }

对于元组支持,我们添加了2个函数。第一个有这个签名:

std::vector<std::vector<int>>

它可以获得我们的回报值(战斗的一半)。要实际执行映射,请使用索引技巧:

  template<
    class... Ts,
    class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
  >
  R operator()( std::tuple<Ts...> const& t ) const

template< class R, class... Ts, size_t... Is > R tup_help( std::index_sequence<Is...>, std::tuple<Ts...> const& t ) const { return std::make_tuple( vectorize<F>{}(std::get<Is>(t))... ); } template< class... Ts, class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... > > R operator()( std::tuple<Ts...> const& t ) const { return tup_help<R>( std::index_sequence_for<Ts...>{}, t ); } 和原始C数组的类似代码应该有效(自然地将原始C数组转换为std::array。)

std::array等是C ++ 14,但很容易编写一个支持C ++ 11中100个元素的版本。 (支持大型数组的版本需要更多工作)。 std::index_sequence别名(以及任何类似的)也是C ++ 14,但是别名很容易用C ++ 11编写,或者只能result_of_t详细说明爆炸。

最后,你typename std::result_of<?>::type并传入任何内容。

如果你想要一个函数而不是一个函数对象,只需让它将工作委托给vectorize<sine_t>{}