我正在寻找一种方法将函数指针,仿函数或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
不仅仅是普通函数指针,还有函子和lambdas?
或者它必须是这样的吗?
template<class F>
void g( F ) { typedef first_argument_of<F>::type T; }
(在我的实际代码中,我需要以这种方式解析一个带有四个参数的函数,但std::function::…argument_type
仅存在一个或两个参数; boost::function
具有argN_type,但我认为我不能无论如何使用它,因为F
并不总是function
这是我的问题,见上文等)
答案 0 :(得分:5)
出于各种原因,没有办法做你想做的事。但是这里应该让问题非常明确:
struct function_object
{
template<typename ...T>
void operator ()(T&&... v){}
};
f( function_object{} );
传递给f
的函数对象的参数类型是什么?没有任何东西,它可以用任何种类和数量的参数调用。
答案 1 :(得分:2)
我还认为没有直接的方法来定义单个主模板定义的模板参数和函数参数,这样可以在所有不同情况下推导出T
(函数指针,lambda表达式,{{ 1}}论证等。)。
因此,我建议您按照问题末尾提出的方法进行操作。事实上,std::function
和Boost提供的工具(据我所知)都不会轻易实现这一点。
我使用的(我从过去的其他SO帖子中学到的)是一个相当复杂的模板std::function
,具有针对所有不同情况的专业化。我的定义是:
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
通过所有这些定义(在下面,我假设您放入一个单独的标题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;
),您可以轻松地定义您的包装函数,如下所示:
more_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;
}
的定义很复杂,因为需要许多不同的专业化。但是如果你想让功能包装更方便的话,这是值得的。
答案 2 :(得分:1)
让我假设我在评论中读到OP确实想要一个改变T
的函数,并把它变成一个改变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";
}
事实上,将参数转换为std::function
,或强制将其存储在std::function
中,会降低成本效率,增加代码的复杂性,并使其无法执行这件事没有问题。
以上版本在打包到std::function
之前,可以在set
,list
,vector
s,原始C
数组上运行,std::array
等等。