6

我通过以下方式创建了一个包含 astd::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 };
}

但是,我无法创建make_foo以 lambda 作为参数的重载:

template<typename Ret, typename... Args>
auto make_foo(??? func)
    -> Foo<Ret, Args...>
{
    return { std::function<Ret(Args...)>(func) };
}

我只是找不到从 lambda 自动推导出返回类型和参数类型的方法。有没有一种惯用的方法来解决这样的问题?

4

2 回答 2

4

好吧,所以我以为我会死,但我终于设法做到了ç_ç

首先,我使用了通常的索引。由于我没有官方索引,所以我使用了几个月前写的旧索引:

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 上找到的函数特征。它们很好,我认为它们相当于评论中链接的 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!
}

这是活生生的例子:)

于 2013-11-12T16:06:40.187 回答
2

我有一个适用于mutablelambda 的示例。我不太清楚如何正确获得 CV 会员资格。

首先,这是我们需要的函数模板:

#include <functional>

template <typename R, typename ...Args>
void foo(std::function<R(Args...)> f)
{ }

现在我们让一个函数模板bar接受一个任意的 lambda,并foo通过检查 lambda 的类型来调用正确版本的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具有此修改特征的普通 lambda,但我不喜欢将函数类型拼写出来:

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 上这不起作用,因为__attribute__((const))在 lambda 的运算符类型上设置了一些奇怪的东西,这似乎干扰了模板专业化。

于 2013-11-12T01:12:18.523 回答