8

I have this variadic struct for determining the sum of the sizeof all the types passed in:

template <typename U, typename... T> struct TotalSizeOf 
    : std::integral_constant<size_t, sizeof(U) + TotalSizeOf<T...>::value> {};

template <typename U> struct TotalSizeOf<U> 
    : std::integral_constant<size_t, sizeof(U)> {};

Usage: TotalSizeOf<double, int, char>::value

The question is, how do I modify this to allow it to work on an empty parameter pack, to return 0;

e.g. TotalSizeOf<>::value

Currently, I get the error error: wrong number of template arguments (0, should be at least 1)

I only have C++14 available.

Raffallo
  • 651
  • 8
  • 19
Salgar
  • 7,687
  • 1
  • 25
  • 39
  • Could you define a default template argument and create a class which would return 0 for sizeof? but I think the second is impossible. Maybe with an empty array like here: https://stackoverflow.com/questions/47352663/how-can-this-structure-have-sizeof-0 – RoQuOTriX Feb 14 '20 at 12:20

2 Answers2

12

You simply must specialize also for <>

Example:

template < typename... T> struct TotalSizeOf;

template < typename U, typename... T> struct TotalSizeOf<U, T...>
: std::integral_constant<size_t, sizeof(U) + TotalSizeOf<T...>::value> {};

template <> struct TotalSizeOf<> :
std::integral_constant<size_t, 0 > { };

int main()
{
    std::cout << TotalSizeOf< int, char>::value << std::endl;
    std::cout << TotalSizeOf< char>::value << std::endl;
    std::cout << TotalSizeOf< >::value << std::endl;
}
Klaus
  • 24,205
  • 7
  • 58
  • 113
  • Thanks, I was specializing incorrectly because I didn't have the top line as the base unspecialized version like you do. – Salgar Feb 14 '20 at 12:38
  • @Salgar: Quite common failure :-) This results in "specialize for wrong number of template arguments"... BTW: As I wrote the example I run also into it :-) – Klaus Feb 14 '20 at 12:39
5

With C++17 you can get this without elaborate template metaprogramming, using fold expressions:

#include <iostream>
#include <type_traits>

template<class... T> 
struct TotalSizeOf: std::integral_constant<std::size_t, (0 + ... + sizeof(T))> {};

int main()
{
    std::cout << TotalSizeOf< int, char>::value << std::endl;
    std::cout << TotalSizeOf< char>::value << std::endl;
    std::cout << TotalSizeOf< >::value << std::endl;
}

This should also be more efficient when compiling (of course at runtime, these are the same).

PS: Just read, that you only have C++14, but will let this stand here, since I think it is nice to see, that we are less forced to do awkward TMP in newer C++ versions.

Addendum: Less elegant than C++17, but C++14 and pretty much tmp-free

#include <iostream>
#include <type_traits>
#include <initializer_list>

constexpr size_t sum(std::initializer_list<size_t> arr) {
    // Accumulate is sadly not constexpr in C++14
    auto ret = 0ul;
    for(auto i: arr) {
        ret += i;
    }
    return ret;
}

template<class... T> 
struct TotalSizeOf: std::integral_constant<std::size_t, sum({sizeof(T)...})> {};

int main()
{
    std::cout << TotalSizeOf< int, char>::value << std::endl;
    std::cout << TotalSizeOf< char>::value << std::endl;
    std::cout << TotalSizeOf< >::value << std::endl;
}
n314159
  • 4,990
  • 1
  • 5
  • 20