如何实现 "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,它从需求列表中给出一个布尔表达式。)