如何实现 "either nested type or void" 特征?
How to implement a "either nested type or void" trait?
我正在处理一些可能定义了嵌套类型的 类。每当未定义此嵌套类型时,我都会假装此嵌套类型为 void
。例如,这里是应该工作的代码:
struct HasNestedTypeDefined {
using Nested = int;
};
struct VoidNestedTypeDefined {
using Nested = void;
};
struct NoNestedTypeDefined {
};
static_assert(!std::is_same_v<reveal_nested_type< HasNestedTypeDefined>::type, void>);
static_assert( std::is_same_v<reveal_nested_type< HasNestedTypeDefined>::type, int>);
static_assert( std::is_same_v<reveal_nested_type<VoidNestedTypeDefined>::type, void>);
static_assert(!std::is_same_v<reveal_nested_type<VoidNestedTypeDefined>::type, int>);
static_assert( std::is_same_v<reveal_nested_type< NoNestedTypeDefined>::type, void>);
static_assert(!std::is_same_v<reveal_nested_type< NoNestedTypeDefined>::type, int>);
到目前为止我已经开始工作了:
template<typename T, typename R = void>
struct enable_if_type {
using type = R;
};
template<typename T, typename = void>
struct reveal_nested_type {
using type = void;
};
template<typename T>
struct reveal_nested_type<T, typename enable_if_type<typename T::Nested>::type> {
using type = typename T::Nested;
};
我认为 SFINAE 即使不使用 enable_if_type
也应该可以工作,就像这样:
template<typename T, typename = void>
struct reveal_nested_type {
using type = void;
};
template<typename T>
struct reveal_nested_type<T, typename T::Nested> {
using type = typename T::Nested;
};
但是在这种情况下从不使用偏特化,即使定义了 typename T::Nested
。为什么SFINAE忽略它?后续问题:是否有更惯用的方法来实现相同的方法?
记下你的最后一次尝试
template<typename T, typename = void>
struct reveal_nested_type {
using type = void;
};
template<typename T>
struct reveal_nested_type<T, typename T::Nested> {
using type = typename T::Nested;
};
实际上对 reveal_nested_type<VoidNestedTypeDefined>
有效。发生的事情是,由于主 reveal_nested_type
class 模板上的默认模板参数,reveal_nested_type<T>
与 reveal_nested_type<T, void>
的含义相同。因此,部分特化 reveal_nested_type<T, typename T::Nested>
仅在嵌套类型存在时才会匹配 并且 嵌套类型与 void
.
相同
这里 std::void_t
是解决此问题的好方法:
template<typename T, typename = void>
struct reveal_nested_type {
using type = void;
};
template<typename T>
struct reveal_nested_type<T, std::void_t<typename T::Nested>> {
using type = typename T::Nested;
};
这是void_t
的典型用法,只是使用SFINAE要求依赖类型有效,and/or依赖表达式有效(通过依赖类型decltype(expr)
),但忽略该类型实际是什么。
以上是我可能用于 C++03 到 C++17 的基本内容。但是如果你可以使用 C++20 约束,另一种方法是:
template <typename T>
struct reveal_nested_type {
using type = void;
};
template <typename T> requires requires { typename T::Nested; }
struct reveal_nested_type<T> {
using type = typename T::Nested;
};
(是的,两个 requires
关键字。第一个在需要布尔表达式的模板上引入约束,第二个引入 requires-expression,它从需求列表中给出一个布尔表达式。)
我正在处理一些可能定义了嵌套类型的 类。每当未定义此嵌套类型时,我都会假装此嵌套类型为 void
。例如,这里是应该工作的代码:
struct HasNestedTypeDefined {
using Nested = int;
};
struct VoidNestedTypeDefined {
using Nested = void;
};
struct NoNestedTypeDefined {
};
static_assert(!std::is_same_v<reveal_nested_type< HasNestedTypeDefined>::type, void>);
static_assert( std::is_same_v<reveal_nested_type< HasNestedTypeDefined>::type, int>);
static_assert( std::is_same_v<reveal_nested_type<VoidNestedTypeDefined>::type, void>);
static_assert(!std::is_same_v<reveal_nested_type<VoidNestedTypeDefined>::type, int>);
static_assert( std::is_same_v<reveal_nested_type< NoNestedTypeDefined>::type, void>);
static_assert(!std::is_same_v<reveal_nested_type< NoNestedTypeDefined>::type, int>);
到目前为止我已经开始工作了:
template<typename T, typename R = void>
struct enable_if_type {
using type = R;
};
template<typename T, typename = void>
struct reveal_nested_type {
using type = void;
};
template<typename T>
struct reveal_nested_type<T, typename enable_if_type<typename T::Nested>::type> {
using type = typename T::Nested;
};
我认为 SFINAE 即使不使用 enable_if_type
也应该可以工作,就像这样:
template<typename T, typename = void>
struct reveal_nested_type {
using type = void;
};
template<typename T>
struct reveal_nested_type<T, typename T::Nested> {
using type = typename T::Nested;
};
但是在这种情况下从不使用偏特化,即使定义了 typename T::Nested
。为什么SFINAE忽略它?后续问题:是否有更惯用的方法来实现相同的方法?
记下你的最后一次尝试
template<typename T, typename = void>
struct reveal_nested_type {
using type = void;
};
template<typename T>
struct reveal_nested_type<T, typename T::Nested> {
using type = typename T::Nested;
};
实际上对 reveal_nested_type<VoidNestedTypeDefined>
有效。发生的事情是,由于主 reveal_nested_type
class 模板上的默认模板参数,reveal_nested_type<T>
与 reveal_nested_type<T, void>
的含义相同。因此,部分特化 reveal_nested_type<T, typename T::Nested>
仅在嵌套类型存在时才会匹配 并且 嵌套类型与 void
.
这里 std::void_t
是解决此问题的好方法:
template<typename T, typename = void>
struct reveal_nested_type {
using type = void;
};
template<typename T>
struct reveal_nested_type<T, std::void_t<typename T::Nested>> {
using type = typename T::Nested;
};
这是void_t
的典型用法,只是使用SFINAE要求依赖类型有效,and/or依赖表达式有效(通过依赖类型decltype(expr)
),但忽略该类型实际是什么。
以上是我可能用于 C++03 到 C++17 的基本内容。但是如果你可以使用 C++20 约束,另一种方法是:
template <typename T>
struct reveal_nested_type {
using type = void;
};
template <typename T> requires requires { typename T::Nested; }
struct reveal_nested_type<T> {
using type = typename T::Nested;
};
(是的,两个 requires
关键字。第一个在需要布尔表达式的模板上引入约束,第二个引入 requires-expression,它从需求列表中给出一个布尔表达式。)