简体   繁体   中英

Select member type of class template based on template type?

I have a class template that has members of some type. This type is determined based on the type that is provided when instantiating the template. It uses a default (double in the example below) unless an override is provided by that class. Classes used as template types may provide this override type (here "Two" provides the override type "int"). If a class provides the override, the override should only be used if the class also sets the UseOverride flag. If flag is absent or false, default "double" should be used.

Problem is that if the template type does not provide the "type", then compiler gives error in below code. I suspect I need to use SFINAE here, but haven't been able to figure out a suitable approach for it, even after puzzling and browsing related questions for a good part of the afternoon.

How to define the EventType template so that it works as intended? I want to keep the EventType<T> syntax.

#include <iostream>

struct One {
    //This type is ignored, and double is used, because UseOverride = true is not specified:
    using type = short;
};
struct Two {
    static constexpr bool UseOverride = true;
    using type = int;
};

struct Three {
    static constexpr bool UseOverride = false;
    //I don't want the compiler to complain that "type" is not available here (because it should default to double anyhow since 
    //the class instructs not to use the override). But compile does generate error. 
    //How to avoid this?
};

template <typename T, typename = void>
struct overrideInfoProvided : std::false_type {};
template <typename T>
struct overrideInfoProvided<T, decltype((void)T::UseOverride, void())> : std::true_type {};

template <typename T>
constexpr bool Override()
{
    if constexpr (overrideInfoProvided<T>::value)
    {
        return T::UseOverride;
    }
    return false;
}

template<class T>
using EventType = typename std::conditional_t<Override<T>(), typename T::type, double>;


template <class T>
struct Test
{
    typename EventType<T> member;
    Test()
    {
        std::cout << member << std::endl;
    }
};

int main()
{
    Test<One>();
    Test<Two>();   
    //Gives error:
    //Test<Three>();// `type': is not a member of any direct or indirect base class of `three'; 
}

I don't want the compiler to complain that "type" is not available here (because it should default to double anyhow since the class instructs not to use the override). But compiler does generate error. How to avoid this?

Just defer the access to ::type with the below type_identity helper:

template <typename T>
struct type_identity { using type = T; };

template <typename T>
using EventType = typename std::conditional_t<Override<T>()
                                            , T
                                            , type_identity<double>>::type;
//                                            ~~~~~~~~~~~~^        

DEMO

You are on the right track, but you don't need to have separate checks for the existence of useOverride , and type . Instead, you can do both of the checks in the same sfinae class:

template <typename T, typename = void, typename = void>
struct EventType_T { 
  using t = double;    // default if useOverride or type doesn't exist
};

template <typename T>
struct EventType_T<T, std::void_t<decltype(T::UseOverride)>,
                      std::void_t<typename T::type>> { 
  // choose type if useOverride is true, and double otherwise
  using t = std::conditional_t<T::UseOverride, typename T::type, double>; 
};

template <typename T>
using EventType = typename EventType_T<T>::t;

Here's a demo . This allows you to still use the EventType<T> syntax as before.

Note, the t member instead of type is unconventional, but since we are already testing for a type member in T , this might make it clearer what's going on. I would recommend using type once yo understand how the solution works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM