使用参数包将lambda转换为std :: function

How*_*ard 10 c++ lambda variadic-templates c++17

关于将lambda转换为std::functions 有关的SO有几个问题,但我还没有看到一个使用参数包作为参数列表的问题.这似乎打破了我的g ++版本(7.1.1-4),可能它不受支持.这是合法的c ++ 17(按标准)吗?如果没有,为什么?

#include <functional>

template <typename TReturn, typename ... TArgs>
void Functor(std::function<TReturn (TArgs...)> f) {}

int main(int argc, char * argv[]) {
    auto x = [] (int a, int b) { return a * b; };
    Functor<int, int, int>(x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

上面的代码将无法编译,因为它类型扣除失败.显然明确打字xstd::function<int (int, int)>,而不是使用auto使错误消失.但这不允许我Functor按照我的意愿传递r值.我还想通过使用函数类型的另一个模板参数来放弃任何类型安全性.

我真正不明白的是为什么上面的代码无法编译,但下面的代码很好并且有效:

#include <functional>

template <typename TReturn, typename TArgA, typename TArgB>
void Functor(std::function<TReturn (TArgA, TArgB)> f) {}

int main(int argc, char * argv[]) {
    auto x = [] (int a, int b) { return  a * b; };
    Functor<int, int, int> (x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

eca*_*mur 12

问题是编译器不知道你打算int, int成为整体TArgs,因此试图推断出TArgs参数的其余部分f.

例如,这将是有效的:

Functor<int, int, int>(std::function<int(int, int, char, float)>{});
// TArgs := {int, int, [...]                       char, float}
Run Code Online (Sandbox Code Playgroud)

因此,您需要指示编译器不要尝试推断其余部分TArgs.例如,你可以写:

(*Functor<int, int, int>)(x);
Run Code Online (Sandbox Code Playgroud)

或者您可以Functor使用未分解的签名编写Sig:

template <Sig>
void Functor(std::function<Sig> f) {}
Run Code Online (Sandbox Code Playgroud)

或者您可以在非推导的上下文TArgs中包含参数的使用f:

template <typename TReturn, typename ... TArgs>
void Functor(std::function<std::conditional_t<false, void, TReturn (TArgs...)>> f) {}
Run Code Online (Sandbox Code Playgroud)


Bar*_*rry 6

这失败了:

#include <functional>

template <typename TReturn, typename ... TArgs>
void Functor(std::function<TReturn (TArgs...)> f) {}

int main(int argc, char * argv[]) {
    auto x = [] (int a, int b) { return a * b; };
    Functor<int, int, int>(x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

因为你没有指明整个TArgs...{int, int}.你正在做的是指定前两种类型{int, int}.实际上,通过提供这三种类型,我们将演绎问题转化为:

template <typename ... TArgs>
void Functor(std::function<int(int, int, TArgs...)> f) {}

int main(int argc, char * argv[]) {
    auto x = [] (int a, int b) { return a * b; };
    Functor(x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这不能编译,因为lambda不是std::function(或从一个派生而来),这与你在没有提供任何类型的情况下无法调用它的原因相同.

非变量版本没有这个问题,因为你已经提供了所有类型.


但实际上,你想要的是:

template <typename F>
void Functor(F ) {}
Run Code Online (Sandbox Code Playgroud)

这不会失去任何类型的安全性.它正在使用std::function丢失类型信息,因为该类模板存在以键入erase.