考虑以下基于 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_t
和 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>>;
组合两个尾随返回类型需要写出所有两个表达式...
否定
使用类型特征,很容易检查一个类型不满足一个特征。那只是!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); }
0
和 int/...
有点奇怪吧?
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/