有没有办法根据 lambda 参数返回类型部分专用化我的模板化函数?

Is there a way to partially specialize my templated function based on a lambda parameter return type?

本文关键字:我的 专用 函数 返回类型部 参数 lambda 有没有      更新时间:2023-10-16

我编写了一个模板化函数,旨在接受lambda和一包参数。我已经声明了函数的返回类型是 lambda 的返回类型。有没有办法将我的模板化函数专门用于某些 lambda 参数返回类型?

我的代码的工作部分如下:

template <typename F, typename ... T>
auto crudeProfile(F f, T ... args) -> decltype(f(args...)) {...}

这符合我的预期。但我想将其行为专门用于返回 void 的 lambda。到目前为止,我只提出了以下代码:

template <typename F, typename ... T>
void crudeProfile(F f, T ... args) {...}

但是当我尝试使用它时,编译器会抱怨这一点:

1>Source.cpp(684): error C2668: 'crudeProfile': ambiguous call to overloaded function
1>  Source.cpp(637): note: could be 'void crudeProfile<main::<lambda_a7118596c99e3162db30942634c4e81e>,>(F)'
1>          with
1>          [
1>              F=main::<lambda_a7118596c99e3162db30942634c4e81e>
1>          ]
1>  Source.cpp(624): note: or       'void crudeProfile<main::<lambda_a7118596c99e3162db30942634c4e81e>,>(F)'
1>          with
1>          [
1>              F=main::<lambda_a7118596c99e3162db30942634c4e81e>
1>          ]
1>  Source.cpp(684): note: while trying to match the argument list '(main::<lambda_a7118596c99e3162db30942634c4e81e>)'

即使是返回非 void 的 lambda 也会导致此错误,尽管错误会略有变化,读作"可能是'类型'或 void"(其中 'type' 是 lambda 返回类型是什么(。

有没有办法部分地专门化我的模板化函数?

不,你不能,但有一个解决方法。

升级(到 C++17(对我来说可能不会有什么坏处......

因此,在 C++17 中,多亏了if constexpr,您可以为此编写一个非常简单的解决方法,而不会过多地损害您的代码,如下所示:

template <typename F, typename ... Ts>
decltype(auto) crudeProfile(F f, Ts ... args)
{
if constexpr (std::is_same_v<std::invoke_result_t<F, Ts...>, void>)
{
// void
}
else
{
// not void
}
}

你不能部分地专门化函数模板,重载也是不可能的1,所以你必须求助于类。请注意,如果您有权访问 C++17,则可以使用if constexpr来简化此操作。

template <typename F, typename = void, typename... Ts>
struct helper {
auto operator()(F f, Ts... args) -> decltype(f(args...)) {
// your code
}
};
template <typename F, typename... Ts>
struct helper<F, typename std::result_of<F(Ts...)>::type, Ts...> {
void operator()(F f, Ts... args) {
// specialization for void
}
};
template <typename F, typename... Ts>
auto crudeProfile(F f, Ts... args) -> decltype(f(args...)) {
return helper<F, Ts...>(f, args...);
}

注意:std::result_of已替换为std::invoke_result,因此,如果可以改用它 (C++17(,请改用它。


1请注意,您仍然可以使用 SFINAE,但我不喜欢此选项,因为这需要您复制一个二进制条件(即调用f(args...)的结果是无效的(。

如果要这样做而不是通过帮助程序,只需向两个重载添加一个额外的模板参数:typename std::enable_if<!std::is_same<void, typename std::result_of<F(Ts...)>::type>::value>::type* = nullptr>.不要忘记删除第二次重载的否定。