检查是否存在嵌套类型别名并有条件地设置类型别名

Check for existence of nested type alias and conditionally set type alias

我想知道如何根据输入参数中类型别名的存在有条件地设置类型别名。

struct a { using type = int; }

template <typename T> struct wrapper {
    using inner_t = ???how???; // if T::type exists, use T::type, else T
};

static_assert(std::is_same<wrapper<int>::inner_t, int>, "expected int");
static_assert(std::is_same<wrapper<a>::inner_t, int>, "expected inner type: int");

如果 type 别名的存在是先决条件,那么一个天真的尝试就像 std::conditional<std::is_class<T>, T::type, T>,但远非安全,缺少对 is_alias_present<T, type> 之类的检查。除此之外,T::type 可能并非对所有类型都存在,从而导致编译器错误。

显然 std::experimental::is_detected 看起来很有希望,但不幸的是现在不是一个选择。

不确定 boost::mp11boost::hana 等元编程库是否是完成该任务的好方法。

借助 C++20 概念

template<class T>
struct innter {
  using type = T;
};

template<class T>
  requires requires { typename T::type; }
struct innter<T> {
  using type = T::type;
};

template <typename T> 
struct wrapper {
  using innter_t = innter<T>::type; // if T::type exists, use T::type, else T
};

在C++17中,可以使用void_t来检测T::type

的有效性
#include <type_traits>

template<class T, class = void>
struct innter {
  using type = T;
};

template<class T>
struct innter<T, std::void_t<typename T::type>> {
  using type = typename T::type;
};

template <typename T> 
struct wrapper {
  using innter_t = typename innter<T>::type; // if T::type exists, use T::type, else T
};

Demo

一个选择是写一个类型特征,比如

template <typename T, typename = void >
struct type_member
{
    using type = T;
};
 
template <typename T >
struct type_member<T, std::void_t<typename T::type>>
{
    using type = typename T::type;
};

template <typename T >
using type_member_t = type_member<T>;

然后你会像

一样在包装器中使用它
template <typename T>
struct wrapper
{
    using type = type_member_t<T>::type;
};

int main()
{
    static_assert(std::is_same_v<typename wrapper<int>::type, int>, "expected int");
    static_assert(std::is_same_v<typename wrapper<a>::type, int>, "expected inner type: int");
}

这是通过利用 SFINAE 来实现的,如果 typename T::typestd::void_t<typename T::type> 中是 ill-formed,那么 type_member 的特化将被丢弃,我们回退到 non-type 成员专业化。

有两件事,检测存在的特征和惰性评估。

在 C++20 中,使用 requires

可以轻松完成特征
template <typename T>
concept has_type = requires { typename T::type; }

然后懒人评价:

template <typename T> struct wrapper {
    using inner_t = std::conditional_t<has_type<T>, T, std::type_identity<T> >::type;
};