Search code examples
c++typesenumsconstraintstype-traits

How to use `std::is_enum` with unnamed `enum`s?


The title is pretty much self explanatory. Here is my situation:

#include <type_traits>

class C1{
    enum{
        c1 = 3
    }
}

class C2{
    enum{
        c2 = 10
    }
}

template<class C>
class C3{
    void do_this();
    void do_that();

    void foo(){
        if constexpr(std::is_enum<C::c1>::value){
            do_this();
        }
        if constexpr(std::is_enum<C::c2>::value){
            do_that();
        }
    }
}

If I'd try to compile this I'd get the error

error: type/value mismatch at argument 1 in template parameter list for ‘template<class _Tp> struct std::is_enum’
note: expected a type, got ‘typename C::c1’

error: type/value mismatch at argument 1 in template parameter list for ‘template<class _Tp> struct std::is_enum’
note: expected a type, got ‘typename C::c2’

So hence my question: is it possible to use std::is_enum with unnamed enums?


Solution

  • C++11 using SFINAE

    You can use decltype to get the type associated with c1 and c2 and then use SFINAE as shown below . C++11 Demo:

    struct C1{
        enum{
            c1 = 3
        };
    };
    
    struct C2{
        enum{
            c2 = 10
        };
    };
    
    template<class C>
    class C3{
        void do_this(){std::cout << "do this called" << std::endl;}
        void do_that(){std::cout << "do that called " << std::endl;}
    public:
        //overload for calling do_this
        template<typename T = C,typename std::enable_if<std::is_same<T, C1>::value, bool>::type = std::is_enum<decltype(T::c1)>::value >void foo()
        {
            do_this();
        }
        //overload for calling do_that
        template<typename T = C,typename std::enable_if<std::is_same<T, C2>::value, bool>::type = std::is_enum<decltype(T::c2)>::value >void foo()
        {
            do_that();
        }
        //overload when none of the conditions are satisfied
        template<typename... T>
        void foo(T...)
        {
            std::cout <<"ellipsis called " << std::endl;
        }
         
    };
    int main()
    {
        C3<C1> c1;
        c1.foo();     //calls do_this() using #1
    
        C3<C2> c2;
        c2.foo();     //calls do_that() using #2
    
        C3<int> c3;
        c3.foo();    //calls the ellipsis version
        
    }
    

    See also C++ 17 demo version that uses std::enable_if_t and std::is_same_v and std::is_enum_v.


    C++20 using concept

    struct C1{
        enum{
            c1 = 3
        };
    };
    template<typename T>
    concept enumC1 = std::is_same_v<T, C1>;
    
    struct C2{
        enum{
            c2 = 10
        };
    };
    template<typename T>
    concept enumC2 = std::is_same_v<T, C2>;
    
    template<class C>
    class C3{
        void do_this(){std::cout << "do this called" << std::endl;}
        void do_that(){std::cout << "do that called " << std::endl;}
    public:
        //overload when none of the conditions are satisfied
        template<typename T = C>
        void foo()
        {
            std::cout <<"general called " << std::endl;
        }
        //overload for calling do_this
        template<enumC1 T = C>void foo()
        {
            do_this();
        }
        //overload for calling do_that
        template<enumC2 T = C>void foo()
        {
            do_that();
        }
        
         
    };
    int main()
    {
        C3<C1> c1;
        c1.foo();     //calls do_this()
    
        C3<C2> c2;
        c2.foo();     //calls do_that()
    
        C3<int> c3;
        c3.foo();    //calls the general version
        
    }
    

    C++ 20 demo