Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
114 views
in Technique[技术] by (71.8m points)

c++ - How to implement a "either nested type or void" trait?

I'm working with some classes that may have a nested type defined. Whenever this nested type is not defined I pretend like this nested type is void. For example, here is the code that should work:

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>);

So far I've got this working:

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;
};

I thought that SFINAE should work even without using enable_if_type, like that:

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;
};

but the partial specialization is never used in this case, even if the typename T::Nested is defined. Why does SFINAE ignore it? The follow up question: is there a more idiomatic way to implement the same?

question from:https://stackoverflow.com/questions/65910307/how-to-implement-a-either-nested-type-or-void-trait

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Note your last attempt

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;
};

actually does work for reveal_nested_type<VoidNestedTypeDefined>. What's happening is that because of the default template argument on the primary reveal_nested_type class template, reveal_nested_type<T> means the same as reveal_nested_type<T, void>. So the partial specialization reveal_nested_type<T, typename T::Nested> will only match if the nested type exists and the nested type is identical to void.

Here std::void_t is a nice way to fix this:

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;
};

This is a typical use of void_t, just using SFINAE to require that a dependent type is valid, and/or that a dependent expression is valid (via the dependent type decltype(expr)), but ignoring what that type actually is.

The above is essentialy what I'd probably use for C++03 to C++17. But if you can use C++20 constraints, another way would be:

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;
};

(Yes, two requires keywords. The first introduces a constraint on the template which expects a Boolean expression, and the second introduces a requires-expression which gives a Boolean expression from a list of requirements.)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...