在元组和向量上递归的非空函数模板
non-void function template recursive over tuples and vectors
我编写了一个函数来计算数字的正弦值。如果它是 std::is_floating_point
,它是 return 的输入类型。但是对于 std::is_integral
,它 return 是 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
模板的大多数示例中,函数没有 return 值,return 是一个累加值,或者具有与 return 相同的类型输入。
我已经对这些主题(以及其他主题)进行了很多实验:
Traversing nested C++11 tuple , , How to make a function that zips two tuples in C++11 (STL)?
最后一个特别有用。我已经为tuple
s工作,但不适用于递归tuples
(tuple
s in tuple
s)。
最终(如果这可能的话),我将不得不使用 gcc 4.9.2 制作 mycos
、mytan
、myasin
等。
**编辑:**这就是我在 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.
首先是一个重载集对象。这很有用,因为它允许我们将整个重载集作为单个对象传递:
struct sine_t {
template<class T>
auto operator()(T&&t)const->
decltype(mysin(std::declval<T>()))
{ return mysin(std::forward<T>(t)); }
};
接下来,我们要"vectorize"一个给定的函数对象。
我们将从简单的开始:
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
的无限递归,我们修改了 std::vector
的 operator()
重载:
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;
}
现在我们支持 std::vector<std::vector<int>>
。
为了支持元组,我们添加了 2 个函数。第一个有这个签名:
template<
class... Ts,
class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
>
R operator()( std::tuple<Ts...> const& t ) const
这让我们获得了 return 价值(成功了一半)。要实际进行映射,请使用索引技巧:
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 );
}
std::array
和原始 C 数组的类似代码应该可以工作(将原始 C 数组自然地转换为 std::array
)。
std::index_sequence
等是 C++14,但在 C++11 中编写支持 100 多个元素的版本很容易。 (支持大型数组的版本需要更多工作)。 result_of_t
别名(和任何类似的)也是 C++14,但别名很容易用 C++11 编写,或者你可以 typename std::result_of<?>::type
冗长爆炸。
最后,你vectorize<sine_t>{}
随便传进去。
如果您想要一个函数而不是一个函数对象,只需让它将工作委托给 vectorize<sine_t>
。
我编写了一个函数来计算数字的正弦值。如果它是 std::is_floating_point
,它是 return 的输入类型。但是对于 std::is_integral
,它 return 是 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
模板的大多数示例中,函数没有 return 值,return 是一个累加值,或者具有与 return 相同的类型输入。
我已经对这些主题(以及其他主题)进行了很多实验:
Traversing nested C++11 tuple ,
最后一个特别有用。我已经为tuple
s工作,但不适用于递归tuples
(tuple
s in tuple
s)。
最终(如果这可能的话),我将不得不使用 gcc 4.9.2 制作 mycos
、mytan
、myasin
等。
**编辑:**这就是我在 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.
首先是一个重载集对象。这很有用,因为它允许我们将整个重载集作为单个对象传递:
struct sine_t {
template<class T>
auto operator()(T&&t)const->
decltype(mysin(std::declval<T>()))
{ return mysin(std::forward<T>(t)); }
};
接下来,我们要"vectorize"一个给定的函数对象。
我们将从简单的开始:
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
的无限递归,我们修改了 std::vector
的 operator()
重载:
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;
}
现在我们支持 std::vector<std::vector<int>>
。
为了支持元组,我们添加了 2 个函数。第一个有这个签名:
template<
class... Ts,
class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
>
R operator()( std::tuple<Ts...> const& t ) const
这让我们获得了 return 价值(成功了一半)。要实际进行映射,请使用索引技巧:
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 );
}
std::array
和原始 C 数组的类似代码应该可以工作(将原始 C 数组自然地转换为 std::array
)。
std::index_sequence
等是 C++14,但在 C++11 中编写支持 100 多个元素的版本很容易。 (支持大型数组的版本需要更多工作)。 result_of_t
别名(和任何类似的)也是 C++14,但别名很容易用 C++11 编写,或者你可以 typename std::result_of<?>::type
冗长爆炸。
最后,你vectorize<sine_t>{}
随便传进去。
如果您想要一个函数而不是一个函数对象,只需让它将工作委托给 vectorize<sine_t>
。