【问题标题】:How to use sfinae to exclude types for which a function is defined?如何使用sfinae排除定义函数的类型?
【发布时间】:2020-11-18 15:36:23
【问题描述】:

考虑如何使用decltype() 尾随返回类型中的逗号表达式来检查是否可以应用函数:

template <class A>
auto f(A a) -> decltype(check_if_possible(a), return_type(a))

如何否定逗号前的部分以排除定义check_if_possible(a) 的情况?

上下文: f() 是不同A 的重载函数。我想解决两个实现之间的模棱两可的重载。其中一个使用check_if_possible(a),另一个在不能应用于a的情况下工作。

编辑: 也欢迎使用std::enable_if 和相关的东西,但我想避免额外的帮助函数/模板,因为有几个类似于f() 的函数具有不同的启用标准。 如果这是不可能的,那么这也是一个答案;)

【问题讨论】:

  • 从C++20开始,可以使用requires

标签: c++ sfinae decltype trailing-return-type


【解决方案1】:

如何否定逗号前的部分以排除定义了 check_if_possible(a) 的情况?

我不知道方法,但是...

上下文:f 是不同 A 的重载函数。我想解决两个实现之间的模棱两可的重载。其中一个使用 check_if_possible(a),另一个在不能应用于 a 的情况下工作。

我通常为这两种情况编写一个f() 函数,这两种情况都需要几个f_helper() 函数接收一个额外的参数。

例如

template <typename A>
auto f (A && a)
 { return f_helper(std::forward<A>(a), 0); }

观察最后一个参数:0,它是一个int

然后你可以写check_if_possible()版本,收到一个额外的int

template <typename A>
auto f_helper (A a, int) -> decltype(check_if_possible(a), return_type(a))
 { /* ... */ }

还有一个通用版本,始终启用,接收额外的long

template <typename A>
auto f_helper (A a, long)
 { /* ... */ }

这样,当A 支持check_if_possible() 时,f_helper() 都启用,但首选特定版本,因为0int,特定版本会收到额外的int(完全匹配)和泛型一个额外的longint 可转换为long,但不是完全匹配)。

A 不支持check_if_possible() 时,只有通用的f_helper() 可用。

【讨论】:

  • 谢谢。我正在考虑优先标签作为替代方案。虽然您的答案在给定的上下文中有效,但我将保留这个问题,因为当可以进行特定呼叫时,sfinae 如何(或是否)可以禁用功能仍未得到解答。
  • @Aaron - 是的......我也很想知道是否有可能以更简单的方式。
【解决方案2】:

您可以依赖 C++17 的 constexpr if 来提供两种不同的实现:

template<class A>
auto f(A a) {
   if constexpr (supportsExpression(a)) {
      // implementation that uses the check_if_possible(a) expression
      // ...
      check_if_possible(a);
      // ...
   } else {
      // implementation that doesn't use the check_if_possible(a) expression
      // ...
   }
}

然后,为supportsExpression()提供两个重载函数模板:

// uses SFINAE
template<typename T>
constexpr auto supportsExpression(T&& arg) ->
   decltype((void)check_if_possible(arg), bool{})
{
   return true;
}

// fallback
template<typename... T>
constexpr bool supportsExpression(T&&...) { return false; }

如果表达式 check_if_possible(arg) 无法编译,第一个重载将因 SFINAE 而被丢弃。

如果由于 SFINAE 而没有从重载集中丢弃第一个重载,那么当在 f() 中调用 supportsExpression() 时,我们最终会得到两个合适的重载。但是,在这种情况下,非可变参数函数模板优先于可变参数模板,即返回 trueA 类型支持表达式)的模板。

【讨论】:

    猜你喜欢
    • 2018-06-10
    • 1970-01-01
    • 2020-08-09
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多