Home > Enterprise >  Check for existence of nested type alias and conditionally set type alias
Check for existence of nested type alias and conditionally set type alias

Time:04-20

I wonder how to conditionally set a type alias, based on the existance of a type alias in input argument like this.

struct a { using type = int; }

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

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

One naive attempt is like std::conditional<std::is_class<T>, T::type, T> if the existence of a type alias is a prerequisite, but is far from being safe, lacking a check for something like is_alias_present<T, type>. Besides that T::type is potentially not existing for all types resulting in a compiler error.

Apearently std::experimental::is_detected looks promising, but unfortunately not an option for now.

Not sure if metaprogramming libraries like boost::mp11 or boost::hana are a good approach to get that done.

CodePudding user response:

With the help of C 20 concepts

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

In C 17, you can use void_t to detect the validity of 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

CodePudding user response:

One option is to write a type trait like

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

and then you would use it in wrapper like

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

This works by leveraging SFINAE and if typename T::type is ill-formed in std::void_t<typename T::type> then that specialization of type_member is discarded and we fall back to the non-type member specialization.

  • Related