合并 void_t 和 enable_if?
Combining void_t and enable_if?
在 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";
};
有没有 shorter/more 优雅的写法,或者“正确的方法”,真的是结合 void_t
和 enable_if
?
std::void_t
的一个重点是可变参数
// ................VVV <- is variadic
template <typename ...>
using void_t = void;
因此当您必须检查多种类型时允许 SFINAE 工作,并允许您在只有一种类型失败时软失败。
如果你只需要检查一个值并且你必须用 std::enable_if
(或类似的类型特征)来检查它,我认为没有理由将它与 [=13= 一起使用].
因此,在您的示例中,"the right way"(恕我直言)避免使用 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()
我更喜欢老方法(但我想这是个人品味的问题)
template <class T>
struct test<T, decltype(std::begin(std::declval<T>(), void())>
{ static constexpr auto text = "has begin iterator"; };
你似乎不明白为什么 std::void_t
是必要的。让我们解决这个问题:)
在您的第一个示例中,如果您不使用 std::void_t
,则永远不会选择偏特化,因为 decltype
会计算为某种类型 T
即不是 void
,因此它不会与部分专业化匹配,并且会退回到一般情况。现在,如果您知道您的函数将始终 return 相同的类型,那么您始终可以更改主模板中的默认参数,但这种方式更容易更改。 (你可以看看我给另一个问题的,这可能有助于理解这一点)。
这就是引入 std::void_t
的原因。 std::void_t
只是一个身份类型特征,无论如何都是 void
。这意味着在您的第一个示例中,无论 decltype
的计算结果是什么,第二个模板参数都将是 void
,因此将匹配并且选择专业化,如果 decltype
是well-formed.
std::enable_if_t
有效,默认情况下 void
当且仅当其中的条件计算为真时。这意味着 std::enable_if_t
已经 return 是 void
无论如何,因此您不需要 std::void_t
到 "transform" 变成了void
,因为已经是void
.
在 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";
};
有没有 shorter/more 优雅的写法,或者“正确的方法”,真的是结合 void_t
和 enable_if
?
std::void_t
的一个重点是可变参数
// ................VVV <- is variadic
template <typename ...>
using void_t = void;
因此当您必须检查多种类型时允许 SFINAE 工作,并允许您在只有一种类型失败时软失败。
如果你只需要检查一个值并且你必须用 std::enable_if
(或类似的类型特征)来检查它,我认为没有理由将它与 [=13= 一起使用].
因此,在您的示例中,"the right way"(恕我直言)避免使用 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()
我更喜欢老方法(但我想这是个人品味的问题)
template <class T>
struct test<T, decltype(std::begin(std::declval<T>(), void())>
{ static constexpr auto text = "has begin iterator"; };
你似乎不明白为什么 std::void_t
是必要的。让我们解决这个问题:)
在您的第一个示例中,如果您不使用 std::void_t
,则永远不会选择偏特化,因为 decltype
会计算为某种类型 T
即不是 void
,因此它不会与部分专业化匹配,并且会退回到一般情况。现在,如果您知道您的函数将始终 return 相同的类型,那么您始终可以更改主模板中的默认参数,但这种方式更容易更改。 (你可以看看我给另一个问题的
这就是引入 std::void_t
的原因。 std::void_t
只是一个身份类型特征,无论如何都是 void
。这意味着在您的第一个示例中,无论 decltype
的计算结果是什么,第二个模板参数都将是 void
,因此将匹配并且选择专业化,如果 decltype
是well-formed.
std::enable_if_t
有效,默认情况下 void
当且仅当其中的条件计算为真时。这意味着 std::enable_if_t
已经 return 是 void
无论如何,因此您不需要 std::void_t
到 "transform" 变成了void
,因为已经是void
.