我通过以下方式创建了一个包含std::function
成员的模板类:
template<typename Ret, typename... Args>
class Foo
{
private:
std::function<Ret(Args...)> _func;
public:
Foo(const std::function<Ret(Args...)>& func):
_func(func)
{}
};
为了不必指定传递函数的参数和返回类型,我创建了一些make_foo
重载:
template<typename Ret, typename... Args>
auto make_foo(Ret (&func)(Args...))
-> Foo<Ret, Args...>
{
return { std::function<Ret(Args...)>(func) };
}
template<typename Ret, typename... Args>
auto make_foo(const std::function<Ret(Args...)>& func)
-> Foo<Ret, Args...>
{
return { func };
}
但是,我无法创建一个以{lambda为参数:
的make_foo
重载
template<typename Ret, typename... Args>
auto make_foo(??? func)
-> Foo<Ret, Args...>
{
return { std::function<Ret(Args...)>(func) };
}
我找不到从lambda自动推导出返回类型和参数类型的方法。是否有解决此类问题的惯用方法?
答案 0 :(得分:3)
好的,所以我以为我会死,但我终于成功了ç_ç
首先,我使用了通常的指数。由于我没有官方版本,我使用了几个月前写的旧索引:
template<std::size_t...>
struct indices {};
template<std::size_t N, std::size_t... Ind>
struct make_indices:
make_indices<N-1, N-1, Ind...>
{};
template<std::size_t... Ind>
struct make_indices<0, Ind...>:
indices<Ind...>
{};
然后,我在StackOverflow上的某个地方找到了一些function traits。它们很好,我认为它们等同于评论中链接的Boost库:
template<typename T>
struct function_traits:
function_traits<decltype(&T::operator())>
{};
template<typename C, typename Ret, typename... Args>
struct function_traits<Ret(C::*)(Args...) const>
{
enum { arity = sizeof...(Args) };
using result_type = Ret;
template<std::size_t N>
using arg = typename std::tuple_element<N, std::tuple<Args...>>::type;
};
然后,我能够编写一个正确的make_foo
函数及其实现函数,因为两者都需要使用索引。小心,这很简单:
template<typename Function, std::size_t... Ind>
auto make_foo_(Function&& func, indices<Ind...>)
-> Foo<
typename function_traits<typename std::remove_reference<Function>::type>::result_type,
typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...>
{
using Ret = typename function_traits<typename std::remove_reference<Function>::type>::result_type;
return { std::function<Ret(typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...)>(func) };
}
template<typename Function, typename Indices=make_indices<function_traits<typename std::remove_reference<Function>::type>::arity>>
auto make_foo(Function&& func)
-> decltype(make_foo_(std::forward<Function>(func), Indices()))
{
return make_foo_(std::forward<Function>(func), Indices());
}
代码有些丑陋且难以理解,但绝对有效。希望它现在不依赖于某些实现定义的行为。另外,感谢大家的建议,它有所帮助! :)
int main()
{
auto lambda = [](int i, float b, long c)
{
return long(i*10+b+c);
};
auto foo = make_foo(lambda);
std::cout << foo(5, 5.0, 2) << std::endl; // 57, it works!
}
这是live example:)
答案 1 :(得分:2)
我有一个与mutable
lambdas一起使用的示例。我无法弄清楚如何获得CV会员资格。
首先,这是我们追求的功能模板:
#include <functional>
template <typename R, typename ...Args>
void foo(std::function<R(Args...)> f)
{ }
现在我们让函数模板bar
接受一个任意的lambda,并通过检查lambda的foo
的类型来调用operator()
的正确版本:
#include <type_traits>
template <typename> struct remove_member;
template <typename C, typename T>
struct remove_member<T C::*>
{ using type = T; };
template <typename F>
void bar(F f)
{
using ft = decltype(&F::operator());
foo(std::function<typename remove_member<ft>::type>(f));
}
示例:
int q;
bar([&](int a, int b) mutable -> int { q = a + b; return q / b; });
你可以使用普通的const
lambdas和这个修改过的特性,虽然我不喜欢拼出这个函数类型:
template <typename C, typename R, typename ...Args>
struct remove_member<R (C::*)(Args...) const>
{ using type = R(Args...); };
如果我使用typename std::remove_cv<T>::type
,我认为它可能适用于原始代码,但至少在GCC上这不起作用,因为在lambda的运算符类型上设置了一些奇怪的__attribute__((const))
干扰模板专业化。