11

I would like to write something like this:

template <class T>
using MyTypeOrTupple = typename std::conditional<has_member_type_MyType<T>::value,
                                                 typename T::MyType,
                                                 std::tuple<> >::type;

I implemented has_member_type_MyType using https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector

However, GCC (4.8.4) still complains about using T::MyType when MyType is not defined in T. Is there a way to solve this?

T.C.
  • 133,968
  • 17
  • 288
  • 421
Roman
  • 1,351
  • 11
  • 26

4 Answers4

13

Do not use that crazy thing from Wikibooks.

template <class T>
using MyType_t = typename T::MyType;

template<class T> 
using MyTypeOrTuple = detected_or_t<std::tuple<>, MyType_t, T>;

Where detected_or_t is std::experimental::detected_or_t from library fundamentals TS v2, and can be implemented as follows:

namespace detail {

    template<class...> struct voidify { using type = void; };
    template<class...Ts> using void_t = typename voidify<Ts...>::type;

    template <class Default, class AlwaysVoid,
              template<class...> class Op, class... Args>
    struct detector {
      using value_t = std::false_type;
      using type = Default;
    };

    template <class Default, template<class...> class Op, class... Args>
    struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
      using value_t = std::true_type;
      using type = Op<Args...>;
    };

} // namespace detail

template <class Default, template<class...> class Op, class... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;

template< class Default, template<class...> class Op, class... Args >
using detected_or_t = typename detected_or<Default, Op, Args...>::type;
T.C.
  • 133,968
  • 17
  • 288
  • 421
6
template <typename...>
struct voider { using type = void; };

template <typename... Ts>
using void_t = typename voider<Ts...>::type;

template <typename, typename = void_t<>>
struct MyTypeOrTupple { using type = std::tuple<>; };

template <typename T> 
struct MyTypeOrTupple<T, void_t<typename T::MyType>> { using type = typename T::MyType; };

template <typename T>
using MyTypeOrTupple_t = typename MyTypeOrTupple<T>::type;

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • 1
    Hi Piotr, could you explain me in short why if I set using void_t = void; compiler fails and why I do need "void_t" as my second type parameter for the template specialization case? – Roman Aug 14 '15 at 12:49
  • 3
    @Roman see [this](http://stackoverflow.com/q/27687389/3953764) and [this](http://stackoverflow.com/q/25833356/3953764) – Piotr Skotnicki Aug 14 '15 at 12:51
2

I think it's required 2 level of indirection:

template<typename ...>
struct void_type
{
    using type = void;
};

template<typename ...T>
using void_t = typename void_type<T...>::type;

#define HAS_TYPE(NAME) \
template<typename, typename = void> \
struct has_type_##NAME: std::false_type \
{}; \
template<typename T> \
struct has_type_##NAME<T, void_t<typename T::NAME>>: std::true_type \
{}

HAS_TYPE(MyType);

template<typename T, bool = has_type_MyType<T>::value>
struct MyTypeOrTupple_impl;

template<typename T>
struct MyTypeOrTupple_impl<T, true>
{ using type = typename T::MyType; };

template<typename T>
struct MyTypeOrTupple_impl<T, false>
{ using type = std::tuple<>; };

template<typename T> using MyTypeOrTupple = typename MyTypeOrTupple_impl<T>::type;

DEMO

Elohim Meth
  • 1,777
  • 9
  • 13
2

Version keeping std::conditional:

template <typename T> struct identity { using type = T; };
template <typename T> struct MyType { using type = typename T::MyType; };

template <class T>
using MyTypeOrTupple =
    typename std::conditional<has_member_type_MyType<T>::value,
                              MyType<T>,
                              identity<std::tuple<>>>::type::type;
Jarod42
  • 203,559
  • 14
  • 181
  • 302