The following code
#include <initializer_list>
#include <vector>
template<int ...>
const std::vector<int>*make_from_ints(int args...)
{ return new std::vector<int>(std::initializer_list<int>{args}); }
is compiling (with GCC 6.3, on Debian/Sid/x86-64) correctly, and I expect it for a call like
auto vec = make_from_ints(1,2,3);
to return a pointer to some vector of integers containing 1, 2, 3.
However, if I replace int
by double
, that is if I add the following (in the same basiletemplates.cc
file ...) code:
template<double ...>
const std::vector<double>*make_from_doubles(double args...)
{ return new std::vector<double>(std::initializer_list<double>{args}); }
I'm getting a compile error:
basiletemplates.cc:8:17: error: ‘double’ is not a valid type
for a template non-type parameter
template<double ...>
^~~
and I don't understand why. After all both int
and double
are scalar numerical POD types (predefined in the C++11 standard).
How to get a template variadic function to be able to code:
auto dvec = make_from_doubles(-1.0, 2.0, 4.0);
and get a pointer to some vector of doubles containing -1.0, 2.0, 4.0 ?
BTW, compiling for C++14 (with g++ -Wall -std=c++14 -c basiletemplates.cc
), and using clang++
(version 3.8.1) instead of g++
dont change anything.
template<int ...>
const std::vector<int>*make_from_ints(int args...)
{ return new std::vector<int>(std::initializer_list<int>{args}); }
The snippet above has a multitude of issues:
Returning a const std::vector<int>*
instead of a std::vector<int>
and unnecessarily using dynamic allocation.
std::make_unique
instead of new
.You defined make_from_ints
to be template function that takes any amount of int
template parameters, but you're not giving those int
s a name - you cannot ever use them!
Your signature is actually being parsed as make_from_ints(int args, ...)
- this is a C va_args
signature that has nothing to do with variadic templates.
type... name
.If you want to accept any number of arguments of a specific type that works nicely with template argument deduction, the easiest way is to use a regular variadic template that accepts an arbitrary amount of types and static_assert
s their type (or uses std::enable_if
for SFINAE-friendliness). Here's an example:
template <typename... Ts>
auto make_from_ints(Ts... xs)
{
static_assert((std::is_same<Ts, int>::value && ...));
return std::vector<int>{xs...};
}
template <typename... Ts>
auto make_from_doubles(Ts... xs)
{
static_assert((std::is_same<Ts, double>::value && ...));
return std::vector<double>{xs...};
}
Usage:
for(auto x : make_from_ints(1,2,3,4)) std::cout << x << " ";
std::cout << "\n";
for(auto x : make_from_doubles(1.0,1.5,2.0,2.5)) std::cout << x << " ";
1 2 3 4
1 1.5 2 2.5
live example on wandbox
Note that I'm using a C++17 fold expression to check if all Ts...
are of a particular type here:
static_assert((std::is_same<Ts, int>::value && ...));
If you do not have access to C++17 features, this can be easily replaced with something like:
template <typename... Ts>
constexpr auto all_true(Ts... xs)
{
for(auto x : std::initializer_list<bool>{xs...})
if(!x) return false;
return true;
}
// ...
static_assert(all_true(std::is_same<Ts, int>{}...));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With