Home > other >  Couldn't deduce template paramter even if it is known at compile time
Couldn't deduce template paramter even if it is known at compile time

Time:06-04

I have a variadic template class SomeClass that looks like this (basically):

template<std::size_t SIZE_>
class SomeClass {
public:
    static constexpr std::size_t SIZE = SIZE_;
};

It has a constexpr member that simply contains the std::size_t template parameter used to instantiate it. I want a constexpr function that can sum all the sizes of SomeClass<SIZE_> specializatins. My first idea was to make a simple variadic template function that adds all the SIZEs like that:

template<typename T>
constexpr std::size_t totalSize() {
    return T::SIZE;
}

template <typename T, typename... Ts>
constexpr std::size_t totalSize() {
    return totalSize<T>()   totalSize<Ts...>();
}

Now I try to call it:

constexpr std::size_t size = totalSize<SomeClass<1>, SomeClass<2>, SomeClass<3>>()    // should return 6

It turns at that the last parameter unpacking makes call to totalSize<T> function ambiguous because both template totalSize functions match it. Fine, I modified my code to have two distinct functions and came up with this:

template<typename T>
constexpr std::size_t totalSizeSingle() {
    return T::SIZE;
}

template <typename T, typename... Ts>
constexpr std::size_t totalSize() {
    std::size_t result = totalSizeSingle<T>();
    if (sizeof...(Ts) > 0) result  = totalSize<Ts...>();
    return result;
}

Again, the last unpacking seem to be problematic. Apparently, T can't be deduced even though it is known at compile time. I get the following error (compiling with GCC):

In instantiation of 'constexpr std::size_t totalSize() [with T = SomeClass<3>; Ts = {}; std::size_t = long long unsigned int]':
main.cpp:129:51:   required from here
main.cpp:134:82:   in 'constexpr' expansion of 'totalSize<SomeClass<1>, SomeClass<2>, SomeClass<3> >()'
main.cpp:129:51:   in 'constexpr' expansion of 'totalSize<SomeClass<2>, SomeClass<3> >()'
main.cpp:129:58: error: no matching function for call to 'totalSize<>()'
  129 |         if (sizeof...(Ts) > 0) result  = totalSize<Ts...>();
      |                                          ~~~~~~~~~~~~~~~~^~
main.cpp:127:23: note: candidate: 'template<class T, class ... Ts> constexpr std::size_t totalSize()'
  127 | constexpr std::size_t totalSize() {
      |                       ^~~~~~~~~
main.cpp:127:23: note:   template argument deduction/substitution failed:
main.cpp:129:58: note:   couldn't deduce template parameter 'T'
  129 |         if (sizeof...(Ts) > 0) result  = totalSize<Ts...>();
      |                                          ~~~~~~~~~~~~~~~~^~

I don't really understand why it is not working. Every type is know at compile time. Why is this happening and how can I get it to work? I'm using C 11.

CodePudding user response:

There's a reason people call structs metafunctions in c template metaprogramming.

In our case the major advantage of doing metaprogramming in a struct is that resolving function overloading byy template arguments is different and more limited than that of class specialisation.

So I suggest:

template <class ...> // only called if there's no arguments
struct add_sizes {
    static constexpr auto value = 0;
};

template <class T, class ... Ts>
struct add_sizes <T, Ts...> {
    static constexpr auto value = T::SIZE   add_sizes<Ts...>::value;
};

// then wrap it into your function:

template <class ... Ts>
constexpr std::size_t totalSize () {
    return add_sizes<Ts...>::value;
}

demo


Another advantage of using structs is for easy "return" of multiple values or types, which for functions gets complicated. Sometimes by calculating A you've already calculated B, so it can make sense to store both.

In general I'd say that if you're ever struggling solving a metaprogramming problem with functions, switch to structs.

CodePudding user response:

The problem is when the template argument list is of size 1 totalSize<Ts...>(); would call totalSize<>() but totalSize<>(); is not defined.

You could make your first version work by making it take 2 or make template arguments.

template<typename T>
constexpr std::size_t totalSize() {
    return T::SIZE;
}

template <typename T, typename U, typename... Ts>
constexpr std::size_t totalSize() {
    return totalSize<T>()   totalSize<U, Ts...>();
}

Demo

  • Related