Can we restrict variadic template arguments to a certain type? I.e., achieve something like this (not real C++ of course):
struct X {};
auto foo(X... args)
Here my intention is to have a function which accepts a variable number of X parameters.
The closest we have is this:
template <class... Args>
auto foo(Args... args)
but this accepts any type of parameter.
Yes it is possible. First of all you need to decide if you want to accept only the type, or if you want to accept a implicitly convertible type. I use
std::is_convertiblein the examples because it better mimics the behavior of non-templated parameters, e.g. along longparameter will accept anintargument. If for whatever reason you need just that type to be accepted, replacestd::is_convertiblewithstd:is_same(you might need to addstd::remove_referenceandstd::remove_cv).Unfortunately, in
C++narrowing conversion e.g. (long longtointand evendoubletoint) are implicit conversions. And while in a classical setup you can get warnings when those occur, you don't get that withstd::is_convertible. At least not at the call. You might get the warnings in the body of the function if you make such an assignment. But with a little trick we can get the error at the call site with templates too.So without further ado here it goes:
The testing rig:
C++20 Concepts
Not here yet, but soon.Available in gcc trunk (March 2020). This is the most simple, clear, elegant and safe solution:We get a very nice error. Especially the
is sweet.
Dealing with narrowing:
I didn't find a concept in the library so we need to create one:
C++17
We make use of the very nice fold expression:
Unfortunately we get a less clear error:
Narrowing
We can avoid narrowing, but we have to cook a trait
is_convertible_no_narrowing(maybe name it differently):C++14
We create a conjunction helper:
please note that in
C++17there will be astd::conjunction, but it will takestd::integral_constantargumentsand now we can have our function:
C++11
just minor tweaks to the C++14 version: