在C++17
中,void_t
允许使用class
/ struct
模板轻松执行SFINAE:
template <class T, class = void>
struct test {
static constexpr auto text = "general case";
};
template <class T>
struct test<T, std::void_t<decltype(std::begin(std::declval<T>())>> {
static constexpr auto text = "has begin iterator";
};
void_t
内的内容是一种类型。我的问题是:当void_t
内的内容是一种类型特征时,如何做同样的事情。使用enable_if
效果很好:
template <class T>
struct test<T, std::void_t<std::enable_if_t<std::is_class_v<T>>> {
static constexpr auto text = "is a class";
};
是否有更短/更优雅的方式来写这个,或“正确的方式”来实现它,真的要结合void_t
和enable_if
?
答案 0 :(得分:2)
std::void_t
的一个重点是可变量
// ................VVV <- is variadic
template <typename ...>
using void_t = void;
因此,当您必须检查多种类型时,允许SFINAE工作,并且当只有其中一种类型失败时允许您软失败。
如果您只需要检查某个值,并且必须使用std::enable_if
(或类似的类型特征)进行检查,我就不会看到与{{1}一起使用它的理由}。
所以,在你的例子中,&#34;正确的方式&#34; (恕我直言)避免使用std::void_t
std::void_t
同样在使用单template <class T>
struct test<T, std::enable_if_t<std::is_class_v<T>>
{ static constexpr auto text = "is a class"; };
的情况下,我更喜欢旧的方式(但我认为这是个人品味的问题)
decltype()
答案 1 :(得分:2)
您似乎不明白为什么std::void_t
是必要的。让我们解决一下:))
在您的第一个示例中,如果您不使用std::void_t
,则永远不会选择部分特化,因为decltype
将评估为某些类型T
而非{ {1}},因此它不会与部分特化相匹配,并且会回归到一般情况。现在,如果您知道函数将始终返回相同类型,则可以随时更改主模板中的默认参数,但这种方式更难以更改。 (您可以查看我提供给另一个问题的answer,这可能有助于理解这一点。)
这就是void
被引入的原因。 std::void_t
只是一种身份类型特征,无论如何都是std::void_t
。这意味着在您的第一个示例中,无论void
的评估结果如何,第二个模板参数都将为decltype
,因此如果void
为decltype
,则匹配并选择专门化良好的。
std::enable_if_t
有效,默认为void
当且仅当其中的条件评估为true时。这意味着std::enable_if_t
已经无论如何都会返回void
,因此您无需std::void_t
将其“转换”为void
,因为它已经是void
。