【问题标题】:How to implement a "either nested type or void" trait?如何实现“嵌套类型或无效”特征?
【发布时间】:2021-01-26 22:38:50
【问题描述】:

我正在处理一些可能定义了嵌套类型的类。每当没有定义这个嵌套类型时,我就假装这个嵌套类型是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;
};

我认为即使不使用enable_if_type,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;
};

但在这种情况下从不使用偏特化,即使定义了typename T::Nested。为什么 SFINAE 会忽略它?后续问题:是否有更惯用的方法来实现相同的功能?

【问题讨论】:

    标签: c++ inner-classes sfinae typetraits


    【解决方案1】:

    记下你的最后一次尝试

    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&lt;VoidNestedTypeDefined&gt;。发生的情况是,由于主 reveal_nested_type 类模板上的默认模板参数,reveal_nested_type&lt;T&gt; 的含义与 reveal_nested_type&lt;T, void&gt; 相同。所以部分特化reveal_nested_type&lt;T, typename T::Nested&gt; 只会在嵌套类型存在时匹配并且嵌套类型与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 要求依赖类型有效,和/或依赖表达式有效(通过依赖类型 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,它从需求列表中给出一个布尔表达式。)

    【讨论】:

    • @DavidStone 感谢您的更正。我应该知道复制/粘贴测试版本,即使是很短的内容。
    猜你喜欢
    • 1970-01-01
    • 2021-07-02
    • 2019-12-08
    • 1970-01-01
    • 2015-01-12
    • 1970-01-01
    • 1970-01-01
    • 2014-09-01
    • 1970-01-01
    相关资源
    最近更新 更多