c++ - void_t 和带有 decltype : are they completely interchangeable? 的尾随返回类型

标签 c++ templates sfinae decltype c++17

考虑以下基于 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 的尾随返回类型,而其对应物不能使用。
最复杂的情​​况可以通过尾随返回类型和重载的组合来解决(例如,当 detector 用于在两个函数之间切换而不是作为触发来禁用或启用某些东西)。

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

最佳答案

这相当于元编程:我应该写一个函数还是只写内联代码。喜欢编写类型特征的原因与喜欢编写函数的原因相同:它更自文档化、可重用、更易于调试。喜欢编写尾随 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>>;

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

否定

使用类型特征,很容易检查一个类型满足一个特征。那只是!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); }

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

static_assert

如果我不想在 SFINAE 上讨论某个概念,而只是想通过明确的信息彻底失败怎么办?

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

概念

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

template <fooable T> void bar(T ) { ... }

关于c++ - void_t 和带有 decltype : are they completely interchangeable? 的尾随返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39552139/

相关文章:

c++ - 指向 char 数组的指针的运行时中断错误

c++ - mysql 远程连接 - mysql CL 客户端可以工作,C++ 应用程序不能

c++ - 如何将模板的实例传递给另一个模板的另一个实例?

C++ 模板 : no matching function for call

C++ 模板特化,明确地调用可能是指针或引用的类型的方法

c++ - 奇怪的 C++ 警告

c++ - 如何获取此文件夹中的文件夹列表?

c++ - 如何检查 C++ 编译时是否存在运算符的特定重载?

c++ - 仅使用 bool 和 char 定义模板类

c++ - 模板化检查是否存在类成员函数?