Search code examples
c++metadatavariadic-templates

How to use templates to build compile-time generated metadata


In trying to call a variadic template, I was getting errors. This question has now been split into the error, and the deeper goal which is in Is it possible to build a const array at compile time using a c++ variadic template?

To test the variadic template in isolation, I tried to just print out the values. So I'm obviously messing up the template somehow:

template<typename T, typename ...Args>
void test(T first, Args... args) {
  cout << sizeof(first) << '\n';
    test(args...);
}
int main() {
  test(123_u32, 1234_u64, 1.23_f32, 1.23456_f64);
}

The error is:

error: no matching function for call to ‘test()’ test(args...);

What I don't understand is, this was taken from Eli Bendersky's blog post, and this works:

template<typename T, typename... Args>
T adder(T first, Args... args) {
  return first + adder(args...);
}

Solution

  • You must remember that templates are templates.

    template<typename T, typename ...Args>
    void test(T first, Args... args) {
      cout << sizeof(first) << '\n';
        test(args...);
    }
    int main() {
      test(123_u32, 1234_u64, 1.23_f32, 1.23456_f64);
    }
    

    This will expand to

    void test(uint32_t first, uint64_t arg1, float32_t arg2, float64_t arg3) {
      cout << sizeof(first) << '\n';
      test(arg1, arg2, arg3);
    }
    void test(uint64_t first, float32_t arg1, float64_t arg2) {
      cout << sizeof(first) << '\n';
      test(arg1, arg2);
    }
    void test(float32_t first, float64_t arg1) {
      cout << sizeof(first) << '\n';
      test(arg1);
    }
    void test(float64_t first) {
      cout << sizeof(first) << '\n';
      test(); //ERROR
    }
    
    int main() {
      test(123_u32, 1234_u64, 1.23_f32, 1.23456_f64);
    }
    

    To tackle this you can create a specialized version that accepts exactly one arguments, that doesn't call test() without arguments.

    template<typename T>
    void test(T first) {
      cout << sizeof(first) << '\n';
    }
    

    The mentioned blog post, does exactly the same.


    Edit:

    As pointed out by @Jarod42 with C++17 and up you don't need to write recursive templates but can directly use the parameter pack using fold expressions:

    template<typename ...Ts> 
    void test(Ts... args) 
    { 
      ((std::cout << sizeof(first) << '\n'), ...);
    }