Lorah Attkins
Lorah Attkins

Reputation: 5856

Why is this variadic template an error?

Just wondering why is this invalid:

#include <iostream>

template <std::size_t... Is>
void foo(Is&&... args) {
    std::cout << "foo called with " << sizeof...(Is) << "params\n"; 
}

int main() {
    foo(1, 2, 3, 4); 
}

It seems a perfectly reasonable example, yet it fails on any compiler I can get my hands on.

If I substitute size_t for class the example works as expected. I've also tried using the new auto template parameter but no online compiler accepts this so I don't know if this an invalid use case or a conformance issue.

Upvotes: 4

Views: 112

Answers (2)

Lorah Attkins
Lorah Attkins

Reputation: 5856

I've been working with another language lately and totally pulled a Monika there. Just to complement Jonathan's answer, (thanks for the explanation and the comments) this is how to ensure all parameters are of size_t type (what I was actually trying to do) using concepts:

template <class... Is>
    requires (std::is_same<Is, int>::value && ...)
void foo(Is&&... args) { /*...*/ }

Or even (ma fav) by defining a dedicated concept

template <class T> concept bool Integer = std::is_same<T, int>::value; 

template <Integer... Is> void foo(Is&&... args) { /*...*/ }
//        ^^^^^^^awesome

Live

Upvotes: 2

Jonathan Wakely
Jonathan Wakely

Reputation: 171433

It's not valid C++, that's why.

If you instantiate that function template with the template arguments 1, 2, 3, 4 then after substituting the arguments into the template you get the signature:

void foo(1&&, 2&&, 3&&, 4&&);

That's clearly not a valid function.

If you want to write a function template that accepts any number of arguments but only if they are of the right type, you can do that like this in C++17:

template<typename T>
  using is_size_t = std::is_same<T, std::size_t>;

template<typename... T>
  std::enable_if_t<std::conjunction<is_size_t<T>...>::value>>
  foo(T&&... args);

Or alternatively (also using C++17):

template<typename... T>
  std::enable_if_t<(std::is_same_v<std::size_t, T> && ...)>
  foo(T&&... args);

For C++14 you need to implement std::conjunction yourself, e.g. using the and_ template from p0032r1

Upvotes: 10

Related Questions