0

I want to write a function that will do an operation based on the types, not arguments, of a function. As such, the function receives no template based arguments. The general gist is something like this:

#include <iostream>

void func() {
    std::cout<<"End marker\n";
}

template <typename Type, typename... T>
void func() {
    std::cout<<"Type sizeof "<<sizeof(T)<<"\n";

    func<T...>();
}

int main() {
    func<int, int, int>();
}

Which, of course, doesn't compile. I've tried doing this:

template <typename Type, typename... T>
void func() {
    std::cout<<"Type sizeof "<<sizeof(T)<<"\n";

    if( sizeof...(T)!=0 )
        func<T...>();
}

This does not work, however. The func<T...> may not get evaluated, but it does need to be compilable.

Is there a way to do this that I'm missing?

NutCracker
  • 11,485
  • 4
  • 44
  • 68
Shachar Shemesh
  • 8,193
  • 6
  • 25
  • 57
  • 2
    `if constexpr`? – Quentin Jan 29 '20 at 13:08
  • Does this answer your question? [recursive variadic template to print out the contents of a parameter pack](https://stackoverflow.com/questions/7124969/recursive-variadic-template-to-print-out-the-contents-of-a-parameter-pack) – user1810087 Jan 29 '20 at 13:20
  • @Quentin, yes, thank you. However, this does not solve the generic case. I need to define a function that will be called with `func<>()`, or this will fail in other ways. – Shachar Shemesh Jan 29 '20 at 13:21
  • @user1810087 in the sense that "it can't be done with functions", then yes, it does :-(. I already have a struct based implementation. It is _considerably_ more complicated. – Shachar Shemesh Jan 29 '20 at 13:23

3 Answers3

2

You can make your original setup work by making func a "template function" (that doesn't actually use the template) like:

template<int = 0>
void func() {
    std::cout<<"End marker\n";
}

template <typename Type, typename... T>
void func() {
    std::cout<<"Type sizeof "<<sizeof(Type)<<"\n";

    func<T...>();
}

And your second one can work by using if constexpr, so func<>() isn't compiled.

Artyer
  • 31,034
  • 3
  • 47
  • 75
1

You can make the non-template function func into a variadic template function which accepts zero template arguments. Then let SFINAE move away this template function when the number of arguments is not zero.

Following should work:

#include <iostream>
#include <type_traits>

template <typename... Ts>
typename std::enable_if<sizeof...(Ts) == 0>::type func() {
    std::cout<<"End marker\n";
}

template <typename T, typename... Ts>
void func() {
    std::cout << "Type sizeof " << sizeof(T) << "\n";

    func<Ts...>();
}

int main() {
    func<int, int, int>();
}

However, please note that:

(8) The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ] The program is ill-formed, no diagnostic required, if: [..] (8.3) every valid specialization of a variadic template requires an empty template parameter pack...

Source here

UPDATE

This would work too:

#include <iostream>
#include <type_traits>

void func() {
    std::cout<<"End marker\n";
}

template <typename T, typename... Ts>
void func() {
    std::cout << "Type sizeof " << sizeof(T) << "\n";

    if constexpr (0 == sizeof...(Ts))
        func();
    else
        func<Ts...>();
}

int main() {
    func<int, int, int>();
}
NutCracker
  • 11,485
  • 4
  • 44
  • 68
  • `template typename std::enable_if::type func()` make the template pedantically ill formed: *"The program is ill-formed, no diagnostic required, if: (8.3) — every valid specialization of a variadic template requires an empty template parameter pack,"*. – Jarod42 Jan 29 '20 at 13:54
  • @Jarod42 tnx for that. I will update my answer with this note. – NutCracker Jan 29 '20 at 14:07
1

With fold-expression (in C++17), you might do:

template <typename... Ts>
void func()
{
    ((std::cout << "Type sizeof " << sizeof(Ts) << "\n"), ...);
    std::cout << "End marker\n";
}

That can even be done in C++11, but it would be more verbose:

template <typename... Ts>
void func()
{
    const int dummy[] = {((std::cout << "Type sizeof " << sizeof(Ts) << "\n"), 0)...};
    static_cast<void>(dummy); // Avoid warning for unused variable.
    std::cout <<"End marker\n";
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302