2

我正在寻找一种将函数指针、仿函数或 lambda 传递给g使用传递函数的参数类型的模板函数的方法,例如:

template<class T1, class T2, class T3>
struct wrapper_t {
  boost::function<void(T1,T2,T3)> f;
  wrapper_t( boost::function<void(T1,T2,T3)> f ) : f(f) {}
  void operator( std::vector<T1> &a, std::vector<T2> &b, T3 c ) {
    assert(a.size() == b.size());
    for(size_t i = 0 ; i != a.size() ; i++) f(a[i], b[i], c);
  }
};
template<class T1, class T2, class T3>
wrapper_t<T1,T2,T3> make_wrapper( boost::function<void(T1,T2,T3)> f ) {
  return wrapper_t<T1,T2,T3>( f );
}

void f(int, double, char) {};
wrapper_t<int, double, char> w0(f); // need to repeat types

auto w1 = make_wrapper(f); // more comfortable

std::vector<int> a{{1, 2, 3}};
std::vector<double> b{{1.0, 2.0, 3.0}};
w0( a, b, 'c' );
w1( a, b, 'c' );

make_wrapper函数仅用于从参数中提取类型,一些语法糖以避免必须键入两次。


我的问题的一个最小示例是这个函数:

template<class T>
void g1( const boost::function<void(T)> & ) {}

使用这些作为输入

void f1(int) {}
struct f2_t { void operator()(int) {} };

它无法推断T=int

f2_t f2;
g1( f1 ); // mismatched types ‘const std::function<void(T)>’ and ‘void(int)’
g1( f2 ); // ‘f2_t’ is not derived from ‘const std::function<void(T)>’
g1( [](int){} ); // ‘::<lambda(int)>’ is not derived from ‘…
g1<int>( f1 ); // ok
g1<int>( f2 ); // ok
g1<int>( [](int){} ); // ok

但是T=int可以从一个普通的函数指针中推断出来,但这也不适用于仿函数或 lambda:

template<class T>
void g2( void (*)(T) ) {}

g2( f1 ); // ok
g2( f2 ); // mismatched types …
g2<int>( f2 ); // ok
g2( [](int){} ); // mismatched types …
g2<int>( [](int){} ); // ok

有没有一种方法T不仅可以推断普通函数指针,也可以推断函子和 lambda?

或者它必须是这样的吗?

template<class F>
void g( F ) { typedef first_argument_of<F>::type T; }

(在我的真实代码中,我需要以这种方式解构一个带有四个参数的函数,但std::function::…argument_type只存在一个或两个参数;boost::function有 argN_type,但我认为无论如何我都不能使用它,因为F这并不总是function我的问题,见上文等)

4

3 回答 3

5

由于各种原因,没有办法做你想做的事。但这里有一个应该使问题非常清楚:

struct function_object
{
    template<typename ...T>
    void operator ()(T&&... v){}
};

f( function_object{} );

传递给的函数对象的参数类型是什么f?没有,可以用任何种类和数量的参数调用它。

于 2013-06-19T01:22:18.953 回答
2

我还认为没有直接的方法来定义单个主模板定义的模板参数和函数参数,这样T可以在所有不同的情况下(函数指针、lambda 表达式、std::function参数等)推导出来。

因此,我建议您遵循问题末尾建议的方法。事实上std::function,Boost 提供的工具(据我所知)都不会轻易实现这一点。

我使用的(我从过去的其他 SO 帖子中了解到)是一个相当复杂的模板function_traits,具有针对所有不同情况的专业化。我的定义是这样的:

template <typename T>
struct function_traits
  : public function_traits<decltype(&T::operator())>
{ };

template <typename Return, typename... Args>
struct function_traits<Return(Args...)>
{
  typedef std::size_t size_type;
  typedef Return      result_type;
  typedef result_type function_type(Args...);

  static constexpr size_type arity = sizeof...(Args);


  template <size_type index>
  struct arg
  {
    typedef typename std::tuple_element<index,std::tuple<Args...>>::type type;
  };

  static constexpr bool value = true;
};

template <typename Return, typename... Args>
struct function_traits<Return(*)(Args...)>
  : function_traits<Return(Args...)>
{ };

template <typename Return, typename... Args>
struct function_traits<Return(&)(Args...)>
  : function_traits<Return(Args...)>
{ };

template <typename Class, typename Return, typename... Args>
struct function_traits<Return(Class::*)(Args...)>
  : function_traits<Return(Args...)>
{ };

template <typename Class, typename Return, typename... Args>
struct function_traits<Return(Class::*)(Args...) volatile>
  : function_traits<Return(Args...)>
{ };

template <typename Class, typename Return, typename... Args>
struct function_traits<Return(Class::*)(Args...) const>
  : function_traits<Return(Args...)>
{ };

template <typename Class, typename Return, typename... Args>
struct function_traits<Return(Class::*)(Args...) const volatile>
  : function_traits<Return(Args...)>
{ };

为了更方便地使用它,您可能需要定义using-aliases:

template <typename Fun>
using result_of = typename function_traits<Fun>::result_type;

template <typename Fun, std::size_t index>
using arg = typename function_traits<Fun>::template arg<index>::type;

使用所有这些定义(在下面,我假设您将其放入单独的 headermore_type_traits.hpp中),然后您可以轻松地定义包装函数,如下所示:

#include <iostream>
#include <functional>
#include <type_traits>
#include "more_type_traits.hpp"

template <typename Fun>
using noref = typename std::remove_reference<Fun>::type;

template <typename Fun>
result_of<noref<Fun>> fun(Fun &&argfun)
{
  // Default-initialize the first argument
  arg<noref<Fun>,0> arg {};

  // Call the function
  return argfun(arg);
}

以下(基本上是从您的代码中复制的)然后编译并为我工作:

void f1(int i)
{ std::cout << "f1(" << i << ')' << std::endl; }

struct f2_t
{
  void operator()(int i)
  { std::cout << "f2(" << i << ')' << std::endl; }
};


int main()
{
  fun(f1);

  f2_t f2;
  fun(f2);

  std::function<void(int)> funobj = [](int i)
    { std::cout << "funobj(" << i << ')' << std::endl; };
  fun(funobj);

  fun( [](int i) { std::cout << "lambda(" << i << ')' << std::endl; } ); 

  return 0;
}

显然, 的定义function_traits很复杂,因为需要许多不同的专业化。但是,如果您想让函数包装更方便,那么付出努力是值得的。

于 2013-06-19T02:26:17.280 回答
1

假设我在评论中读到 OP 真的想要一个改变 a 的函数T,并将它变成一个改变 a 的函数std::vector<T>,并且认为为了做到这一点,你需要知道什么T是。

你不

#include <type_traits>
#include <utility>

template<typename Lambda>
struct container_version {
  Lambda closure;
  container_version( container_version const& ) = default;
  container_version( container_version && ) = default;
  container_version( container_version & ) = default;

  template<typename U>
  container_version( U&& func ):closure(std::forward<U>(func)) {};

  // lets make this work on any iterable range:
  template<typename Container>
  void operator()( Container&& c ) const {
    for( auto&& x:c )
      closure(x);
  }
};

template<typename Lambda>
container_version< typename std::decay<Lambda>::type >
make_container_version( Lambda&& closure ) {
  return {std::forward<Lambda>(closure)};
}

#include <vector>
#include <iostream>
#include <functional>
#include <array>

int main() {
   std::vector<int> my_vec = {0, 1, 2, 3};
   for (auto x:my_vec)
      std::cout << x << ",";
   std::cout << "\n";
   make_container_version( []( int& x ) { x++; })( my_vec );

   for (auto x:my_vec)
      std::cout << x << ",";
   std::cout << "\n";

   // hey look, we can store it in a `std::function` if we need to:
   auto super_func = make_container_version( []( int& x ) { x++; } );
   std::function< void( std::vector<int>& ) > func = super_func;
   // and the same super_func can be used for a function on a different container:
   std::function< void( std::array<int,7>& ) > func2 = super_func;

   func(my_vec);
   for (auto x:my_vec)
      std::cout << x << ",";
   std::cout << "\n";
}

实际上,将参数转换为 astd::function或强制将其存储在 a 中std::function,会降低效率,增加代码的复杂性,并使其无法做它没有问题的事情。

上述版本,在打包成 a 之前std::function,可以对sets、lists、vectors、原始C数组、std::arrays 等进行操作。

于 2013-06-20T02:24:36.053 回答