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.)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…