在variadic模板中从lambda隐式转换为std :: function

时间:2017-11-01 11:11:23

标签: c++ c++11 lambda

我想实现一个以lambda为参数的模板函数。

#include <functional>

template<typename ... Result> using Fun = std::function<void(Result ...)>;
template<typename ... Result> void yield(Fun<Result ...>&& body) {};

template <typename T>
struct identity {
    typedef T type;
};

template<typename ... Result> void yield2(typename identity<Fun<Result ...>>::type && body) {};

int main() {
    yield<char>(
        Fun<char>(
            [](char) -> void {} // 1. success
        )
    );

    yield2<char>(
        [](char) -> void {} // 2. success with identify
    );

    yield<char>(
        [](char) -> void {} // 3. fail, seems achievable
    );

    yield(
        [](char) -> void {} // 4. fail, impossible ?
    );

    return 0;
}

为什么案例3失败了?我已经将模板参数提供给模板,因此它应该能够推导出函数类型并将lambda隐式转换为函数

修改

编译器总是从函数参数中减少模板参数,我们可以将其反转吗?

    yield<char>(
        [](auto&& c) -> void {} // 5. is it possible ?
    );

1 个答案:

答案 0 :(得分:4)

问题是你有一个可变参数模板,除了明确的char参数之外,它还会开始尝试查看它还能推断出什么。

如果你有一个像这样的参数模板:

template<typename Result> using Fun = std::function<void(Result)>;
template<typename Result> void yield(Fun<Result>&& body) {};

你会注意到

yield<char>(
    [](char) -> void {} // 3. fail, seems achievable
);

完全没有问题,因为整个std::function都是可以推断的。

但是一旦我们制作了一个可变参数模板,我们的编译器就会变得不快乐:

template<class... Result> using Fun = std::function<void(Result...)>;
template<class... Result> void yield(Fun<Result...>&& body) {};

这是因为,无论喜欢与否,编译器将尝试为Fun<Result...>推导出更多的模板参数,给出传递的([temp.deduct.type] ])。

yield2回避此问题,因为它将结果类型放入非推导上下文中,但由于您明确指定了模板参数,因此它将仅使用那些显式指定的参数({ {1}})推断出类型(#1的工作原理基本相同)。

我认为最好的解决方法是你的char尝试,但是你也可能会这样做以防止参与类型扣除所传递的值:

yield2

另一种解决方法是auto fn = &yield<char>; fn( [](char) -> void {} ); 您对static_cast的正确类型的呼叫: (实际上我只是通过其他可能的解决方法阅读“非推断的上下文是:”[temp.deduct.type])

yield

编辑最后,您可以编写一些额外的样板模板,以使通话看起来更好(接近您的#4)。请记住,这是一个不完整的impl,例如:

此模板的目标是检测lambda的using fn_type = void(*)(Fun<char>&&); static_cast<fn_type>(&yield)( [](char) -> void {} ); 函数并提取其返回类型和参数,以便在调用operator()时我们可以显式指定Fun类型(提取返回类型是不必要的,因为您只使用yield):

首先是一个帮助器结构,它允许我们检测不可变的lambda的返回类型和参数类型:

void

其次,我们的帮助函数template<class T> struct Fun_Type; template<class C, class Ret, class... Args> struct Fun_Type<Ret(C::*)(Args...) const> { using type = Fun<Args...>; }; 将[{1}}传递给对call_yield的调用:

Fun_Type<...>::type

现在我们可以简单地称之为:

yield

Demo(C ++ 11)