Unpacking Variadic Parameter Pack of Enums

96 Views Asked by At

Update: Edited to fix compilation in working example.

I want to do something like the following so the function can take in both a list of the enum class instances or a struct that holds them, but the definition of auto myFunc2<EList<Types...>> fails with expected a constant not E::A

#include <cstdint>
#include <iostream>

enum class E {A = 0, B=1};
template<E... Types> struct tl2 {};
using my_list_2 = tl2<E::A>;

template <E ... Types>
auto myFunc2 = [] {
    std::cout << "Size: " << sizeof...(Types) << std::endl;     
};

template <template<E...> typename EList, E... Types>
auto myFunc2<EList<Types...>> = [] {
    std::cout << "Size: " << sizeof...(Types) << std::endl;     
};

int main() {
    myFunc2<E::A, E::B>();  // This call prints size of typelist
                            //Works when myFunc2<Elist<Types..>> is commented out

    //myFunc2<my_list_2>();      //This breaks      
} 

If we convert this to general types then everything compiles and works as expected. For example:

#include <cstdint>
#include <iostream>

template < typename ... Types > struct tl
{
};
using my_list = tl <int, float, uint64_t>;

template <typename ... Types>
static constexpr auto myFunc2 = [] {
    std::cout << "Size: " << sizeof...(Types) << std::endl;   
};

template <template<class...> class TL, typename ... Types>
static constexpr auto myFunc2<TL<Types...>> = [] {
    std::cout << "Size: " << sizeof...(Types) << std::endl;   
};

int main() {
    myFunc2<int,uint64_t,bool,uint16_t>();
    myFunc2<my_list>();                    
} 

What is going on here? Is there a limitation in how enum classes can be handled as templates?

1

There are 1 best solutions below

0
Davis Herring On BEST ANSWER

True function templates can do this (with the usual extra helper for deducing a pack):

template <E ... Types>
void myFunc2() {
    std::cout << "Size: " << sizeof...(Types) << std::endl;     
}

namespace detail {
template <template<E...> typename EList, E... Types>
void myFunc2(EList<Types...>>) {
    std::cout << "Size: " << sizeof...(Types) << std::endl;     
}
}

template <typename EList>
void myFunc2() {
    detail::myFunc2(EList());
}

This approach allows “one template” to accept template arguments of different kinds because the templates with the wrong kinds of template parameters will simply be ignored during overload resolution.