'Variadic template packed argument to std::vector
I'm new to templates and I don't really undestand why this doesn't work. I expected the vector to be constructed with those values.
main.cpp
template <typename ...T>
void int_printf(T ...args)
{
std::vector<T> vec = {args...};
for(auto& v:vec)
{
std::cout << v << std::endl;
}
}
int main()
{
int_printf(1,2,3,4);
return 0;
}
Expected result
1
2
3
4
Error by msvc compiler (translated)
src/main.cpp(35): error C3520: 'T': the parameter pack must be expanded in this context
src/main.cpp(37): error C3536: '<begin>$L0': can't be used before initialization
src/main.cpp(37): error C3536: '<end>$L0': can't be used before initialization
src/main.cpp(37): error C2100: invalid redirection
Solution 1:[1]
Another, slightly more wordy, way to do this is to add an initial template parameter specifying the type of vec
, like so:
#include <iostream>
#include <vector>
template <typename T, typename ... Args>
void int_printf(Args ... args)
{
std::vector<T> vec = {args...};
for (auto& v : vec)
{
std::cout << v << std::endl;
}
}
int main()
{
int_printf<int>(1,2,3,4);
return 0;
}
This might give clearer error messages if you pass a list of incompatible types to int_printf
.
Solution 2:[2]
The issue in your code, is that T
is not a template parameter in this context, it is a template parameter pack, which would expand to T=[int,int,int,int]
in your example. std::vector
expects a type to be passed as a template parameter, not a template parameter pack. You can solve this issue by using std::common_type
:
#include<type_traits>
template <typename ...T>
void int_printf(T ...args)
{
//use std::common_type to deduce common type from template
// parameter pack
std::vector<typename std::common_type<T...>::type> vec = {args...};
for(auto& v:vec)
{
std::cout << v << std::endl;
}
}
You should note, this will only work if the arguments passed to int_printf
have a common type.
Solution 3:[3]
When you do std::vector<T>
, the T
is not a single type but is instead is a pack of types. You can't use that for the vector because it wants a single type for the element.
There is a couple ways to handle this. First would be to just hard code the type of the vector. This makes the code less generic, but would work for you since your function is called int_printf
and not anything_print
Another option is to use std::common_type
to get the common type of the elements like
template <typename ...T>
void int_printf(T ...args)
{
std::vector<std::common_type_t<T...>> vec = {args...};
for(auto& v:vec)
{
std::cout << v << std::endl;
}
}
You could also use a fold expression and skip the vector entirely like
template <typename ...T>
void int_printf(T ...args)
{
((std::cout << args << std::endl), ...);
// ^^ ^
// | do this part ^ ^ |
// | for each parameter | |
// start fold expression end fold
}
If you want just an unlimited number of int
's you could also use SFINAE to constrain the pack type to be integers like
template <typename ...T, std::enable_if_t<std::conjunction_v<std::is_same<T, int>...>, bool> = true>
void int_printf(T ...args)
{
((std::cout << args << std::endl), ...);
}
and now you can't call this function with anything other than int
's but it can have as many as you want.
Solution 4:[4]
You can use a std::variant
in conjunction with std::vector
. Here is an example:
template<typename... Args>
class VariantTest
{
private:
using ArgTypes = std::variant<Args...>;
std::vector<ArgTypes> elems;
public:
VariantTest()
{
elems.reserve(10); //just a number
}
template<typename... ArgsL>
void AddTypes(ArgsL&&... args)
{
(elems.emplace_back(std::forward<ArgsL>(args)), ...);
}
size_t GetElemsCount()
{
return elems.size();
}
};
int main()
{
VariantTest<A, B> vt;
vt.AddTypes(B(), A()); //Note the order does not matter.
std::cout << "Number of elements: " << vt.GetElemsCount() << '\n';
return 0;
}
You will need C++17.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | |
Solution 4 | user14581033 |