【问题标题】:Is there a way to partially specialize my templated function based on a lambda parameter return type?有没有办法根据 lambda 参数返回类型部分专门化我的模板化函数?
【发布时间】:2018-08-10 02:47:23
【问题描述】:

我编写了一个模板函数,该函数旨在接受一个 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 的 lambdas 也会导致此错误,尽管错误会略有变化,改为“可能是 'type' 或 void”(其中 'type' 是 lambda 返回类型)。

【问题讨论】:

  • 你可以访问 C++17 吗?
  • 升级可能不会对我造成伤害,但现在我相信我使用的是 C++11-MSVC++ 2015,宏 __cplusplus == 199711
  • 我认为即使返回类型为 void 和适当的 RAII,您也可以相同地编写函数。
  • 您可以查看与我的answer 类似的问题。

标签: c++ templates


【解决方案1】:

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

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

升级(到 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
    }
}

【讨论】:

  • 奇怪的是,在 VC2015 上,如果我省略 if 语句的 constexpr 部分,将 is_same_v 更改为 is_same,将 invoke_result_t 更改为result_of&lt;T1,T2&gt;::type。但它有效!
  • @DavidRogers 它可能在没有constexpr 的情况下工作,但在这种情况下,您必须确保由 raw if 拆分的两个实现必须是 NOT 格式错误,因为编译器试图同时评估两者,即使控制流永远不会满足其他条件。 (这是ifif constexpr的显着区别)
【解决方案2】:

你不能部分特化函数模板,重载也是不可能的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&lt;!std::is_same&lt;void, typename std::result_of&lt;F(Ts...)&gt;::type&gt;::value&gt;::type* = nullptr&gt;。不要忘记删除第二个重载的否定。

【讨论】:

  • @DeanSeo 哎呀,对不起。忘记了可变参数后面不能有任何模板参数。固定
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-01
  • 1970-01-01
相关资源
最近更新 更多