6

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
    Err whoops. I edited the first example (which does not compile) to reconstruct the second from memory within the post itself and likely made a mistake. I'll just add a version that does compile now: https://godbolt.org/z/q1d8qvnc8 – AlethicSupporter Jun 15 '23 at 20:40
  • 1
    Ok, I deleted my answer. I need to rethink it I noticed. – Ted Lyngmo Jun 15 '23 at 21:15
  • 2
    In second case, you have partial specialization, whereas in first case, you have redeclaration with different kind, which is forbidden. Example might be simplified to `template int ko = 0; /* ko */ template int ko = 0; /* ko<42> */ ` – Jarod42 Jun 15 '23 at 21:15
  • ok duh. Thanks, that makes sense. So the main solution would be to define my own classes that could be accepted by the partial spec? This is what I ended up working with. ``` namespace E { struct A { static const int value = 0; explicit operator int() const {return value;} }; } ``` – AlethicSupporter Jun 15 '23 at 21:52
  • Or are there other ways to create named type collections/parameter packs that can be reused without changing the function signature? – AlethicSupporter Jun 15 '23 at 22:01
  • 1
    [`std::integral_constant`](https://en.cppreference.com/w/cpp/types/integral_constant) is a way to wrap value in type... – Jarod42 Jun 15 '23 at 22:12
  • There are also options like `std::integer_sequence`, but they are mostly useful if you can allow type deduction through function arguments, which would change the signature. For example, you'd have to write your second function as `template auto myFunc2(std::integer_sequence const&)`. The main issue is that `my_list_2` is not a template: it will only match with a `class T` template parameter unless you leverage argument deduction. – sigma Jun 16 '23 at 16:16

1 Answers1

2

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.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76