C++11 可变参数::函数参数
C++11 variadic std::function parameter
一个名为test
的函数将std::function<>
作为其参数。
template<typename R, typename ...A>
void test(std::function<R(A...)> f)
{
// ...
}
但是,如果我执行以下操作:
void foo(int n) { /* ... */ }
// ...
test(foo);
编译器(gcc 4.6.1(说no matching function for call to test(void (&)(int))
。
要使最后一行test(foo)
编译并正常工作,如何修改test()
函数?在test()
函数中,我需要f
std::function<>
的类型。
我的意思是,是否有任何模板技巧可以让编译器确定函数的签名(例如foo
(,并将其自动转换为std::function<void(int)>
?
编辑
我也想让这个工作适用于 lambdas(有状态和无状态(。
看起来你想使用重载
template<typename R, typename ...A>
void test(R f(A...))
{
test(std::function<R(A...)>(f));
}
这个简单的实现将接受您将尝试传递的大多数(如果不是全部(函数。 外来函数将被拒绝(如void(int...)
(。更多的工作会给你更多的慷慨。
std::function
实现了 Callable 接口,即它看起来像一个函数,但这并不意味着你应该要求可调用对象std::function
s。
template< typename F > // accept any type
void test(F const &f) {
typedef std::result_of< F( args ) >::type R; // inspect with traits queries
}
鸭子类型是模板元编程中的最佳策略。接受模板参数时,要不具体,只让客户端实现接口。
例如,如果您真的需要一个std::function
来重新定位变量或类似的东西,并且您知道输入是原始函数指针,则可以分解原始函数指针类型并将其重组为std::function
。
template< typename R, typename ... A >
void test( R (*f)( A ... ) ) {
std::function< R( A ... ) > internal( f );
}
现在,用户无法传递std::function
,因为它已封装在函数中。您可以将现有代码保留为另一个重载,并仅委托给它,但要注意保持接口简单。
至于有状态的lambdas,我不知道如何处理这种情况。它们不会分解为函数指针,据我所知,参数类型无法查询或推断。这些信息对于实例化std::function
是必要的,无论好坏。
这是一个古老的,我似乎找不到太多关于同一主题的内容,所以我想我会继续写一个笔记。
在GCC 4.8.2上编译,以下作品:
template<typename R, typename... A>
R test(const std::function<R(A...)>& func)
{
// ...
}
但是,您不能仅通过传入指针、lambda 等来调用它。但是,以下 2 个示例都适用于它:
test(std::function<void(int, float, std::string)>(
[](int i, float f, std::string s)
{
std::cout << i << " " << f << " " << s << std::endl;
}));
也:
void test2(int i, float f, std::string s)
{
std::cout << i << " " << f << " " << s << std::endl;
}
// In a function somewhere:
test(std::function<void(int, float, std::string)>(&test2));
这些的缺点应该很明显:你必须为它们显式声明 std::function,这可能看起来有点丑陋。
话虽如此,不过,我把它和一个元组放在一起,这个元组被扩展为调用传入函数,它有效,只是需要多一点明确说明你在调用测试函数做什么。
包含元组的东西的示例代码,如果你想玩它:http://ideone.com/33mqZA
通常不建议按值接受std::function
,除非您处于"二进制分隔"(例如动态库,"不透明"API(,因为正如您刚刚目睹的那样,它们对重载造成了严重破坏。当函数实际上按值获取std::function
时,调用者通常是构造对象以避免重载问题的负担(如果函数被重载(。
但是,由于您编写了模板,因此很可能您没有使用std::function
(作为参数类型(来实现类型擦除的好处。如果你想做的是检查任意函子,那么你需要一些特征。例如,Boost.FunctionTypes具有result_type
和parameter_types
等特征。一个最小的功能示例:
#include <functional>
#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/function_type.hpp>
template<typename Functor>
void test(Functor functor) // accept arbitrary functor!
{
namespace ft = boost::function_types;
typedef typename ft::result_type<Functor>::type result_type;
typedef ft::parameter_types<Functor> parameter_types;
typedef typename boost::mpl::push_front<
parameter_types
, result_type
>::type sequence_type;
// sequence_type is now a Boost.MPL sequence in the style of
// mpl::vector<int, double, long> if the signature of the
// analyzed functor were int(double, long)
// We now build a function type out of the MPL sequence
typedef typename ft::function_type<sequence_type>::type function_type;
std::function<function_type> function = std::move(functor);
}
最后一点,我不建议在一般情况下内省函子(即对它们的结果类型和参数类型进行刺激(,因为这根本不适用于多态函子。考虑几个重载operator()
:那么就没有"规范"结果类型或参数类型。对于 C++11,最好"急切地"接受任何类型的函子,或者根据需要使用 SFINAE 或 static_assert
等技术约束它们,然后(当参数可用时(使用 std::result_of
来检查给定参数集的结果类型。需要预先约束的情况是,当目的是将函子存储到例如std::function<Sig>
容器中时。
要了解我上一段的意思,使用多态函子测试上面的代码片段就足够了。
- 将可变参数函数的参数封装在类实例中
- QML 使用带有参数C++函数
- 使用可变参数函数作为模板参数
- 如何在C++中伪造虚拟可变参数函数模板?
- 为什么可变参数函数不适用于模板
- C++ std::functional 中的可变参数函数模板
- 可变参数函数指针的定义对于VxWorks spyLib来说不清楚
- 使用可变参数函数覆盖具有不同函数签名的虚函数
- 考虑引用和常量的可变参数函数包装器
- 使用可变参数函数将整数和/或整数数组放入单个 int 数组中
- 在可变参数函数中转发特定范围的参数
- 通过引用传递参数;函数返回类型是否必须为 VOID?
- 使用带有一个参数函数的递归找到数字的平方
- 可变参数函数模板不能很好地使用 std::function 作为参数
- 多个可变参数函数的单个模板参数包?
- 参数数据类型未知的可变参数函数
- 可变参数函数参数包扩展
- 使用模板可变参数函数将多个参数传递给另一个函数
- 对可变参数函数的递归调用的链接器错误
- 通过像printf这样的可变参数函数传递一个带有常量字符*转换函数的类