void_t 和带有 decltype 的尾随返回类型:它们完全可以互换吗?

2024-02-19

考虑以下基于的基本示例void_t:

template<typename, typename = void_t<>>
struct S: std::false_type {};

template<typename T>
struct S<T, void_t<decltype(std::declval<T>().foo())>>: std::true_type {};

它可以按如下方式使用:

template<typename T>
std::enable_if_t<S<T>::value> func() { }

使用尾随返回类型可以完成相同的操作decltype:

template<typename T>
auto func() -> decltype(std::declval<T>().foo(), void()) { }

我想到的所有例子都是如此。我没能找到一个案例void_t或尾随返回类型decltype可以使用,而其对应的则不能。
最多complex可以通过尾随返回类型和重载的组合来解决情况(例如,当detector用于在两个功能之间切换,而不是作为禁用或启用某些功能的触发器)。

是这样吗?他们是(void_t and decltype作为尾随返回类型加上重载(如果需要)完全可以互换吗?
否则,在什么情况下无法使用一种方法来解决约束而我被迫使用一种特定的方法?


这相当于元编程:我应该编写一个函数还是应该内联编写代码。更喜欢编写类型特征的原因与更喜欢编写函数的原因相同:它更具自记录性,可重用,更易于调试。喜欢编写尾随 decltype 的原因与喜欢编写内联代码的原因类似:它是一次性的,不可重用,所以为什么要花精力将其分解出来并为它想出一个合理的名称?

但以下是您可能需要类型特征的一系列原因:

重复

假设我有一个特征,我想多次检查。喜欢fooable。如果我写一次类型特征,我可以将其视为一个概念:

template <class, class = void>
struct fooable : std::false_type {};

template <class T>
struct fooable<T, void_t<decltype(std::declval<T>().foo())>>
: std::true_type {};

现在我可以在很多地方使用相同的概念:

template <class T, std::enable_if_t<fooable<T>{}>* = nullptr>
void bar(T ) { ... }    

template <class T, std::enable_if_t<fooable<T>{}>* = nullptr>
void quux(T ) { ... }

对于检查多个表达式的概念,您不希望每次都重复它。

可组合性

随着重复的进行,组合两种不同的类型特征很容易:

template <class T>
using fooable_and_barable = std::conjunction<fooable<T>, barable<T>>;

组合两个尾随返回类型需要写出所有两个表达式......

Negation

通过类型特征,可以很容易地检查类型doesn't满足一个特质。那只是!fooable<T>::value。你不能写一个尾随-decltype用于检查某些内容是否无效的表达式。当您有两个不相交的重载时,可能会出现这种情况:

template <class T, std::enable_if_t<fooable<T>::value>* = nullptr>
void bar(T ) { ... }

template <class T, std::enable_if_t<!fooable<T>::value>* = nullptr>
void bar(T ) { ... }

这很好地引导...

标签发送

假设我们有一个短类型特征,那么用类型特征标记调度会更清晰:

template <class T> void bar(T , std::true_type fooable) { ... }
template <class T> void bar(T , std::false_type not_fooable) { ... }
template <class T> void bar(T v) { bar(v, fooable<T>{}); }

否则的话:

template <class T> auto bar(T v, int ) -> decltype(v.foo(), void()) { ... }
template <class T> void bar(T v, ... ) { ... }
template <class T> void bar(T v) { bar(v, 0); }

The 0 and int/...有点奇怪吧?

static_assert

如果我不想在某个概念上进行 SFINAE,而只想通过明确的信息进行硬失败,该怎么办?

template <class T>
struct requires_fooability {
    static_assert(fooable<T>{}, "T must be fooable!");
};

Concepts

当(如果?)我们得到概念时,显然,当涉及到与元编程相关的所有内容时,实际使用概念要强大得多:

template <fooable T> void bar(T ) { ... }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

void_t 和带有 decltype 的尾随返回类型:它们完全可以互换吗? 的相关文章

随机推荐