I don't understand the error message when trying to pass a variable number of initializer lists:
template<typename... Values>
void foo(Values...)
{
}
int main()
{
foo(1, 2, 3, "hello", 'a'); // OK
foo({1}, {2, 3}); // ERROR
}
The error message complains about too many arguments:
prog.cpp: In function ‘int main()’:
prog.cpp:9:20: error: too many arguments to function
‘void foo(Values ...) [with Values = {}]’
foo({1}, {2, 3});
^
prog.cpp:2:6: note: declared here
void foo(Values...)
^
However, should I not be able to pass as many arguments as I want? [ideone link]
The problem is likely deducibility. {}
could be uniform initializers to any of the arguments.
This works:
#include <initializer_list>
template<typename... Values>
void foo(std::initializer_list<Values>... args)
{
}
template<typename... Values>
void foo(Values&&... args)
{
}
int main()
{
foo(1, 2, 3, "hello", 'a');
foo({1}, {2, 3});
}
See it Live on Coliru
The issue is not with the varadic arguments, but that the compiler cannot deduce the type of a brace enclosed initializer list, except for the case where you've declare the parameter of std::initializer_list<T>
§ 14.8.2.1 Template argument deduction is done by comparing each function
template parameter type (call it P) with the type of the corresponding
argument of the call (call it A) as described below. If removing
references and cv-qualifiers from P gives std::initializer_list
for some P0 and the argument is an initializer list (8.5.4), then
deduction is performed instead for each element of the initializer
list, taking P0 as a function template parameter type and the
initializer element as its argument. Otherwise, an initializer list
argument causes the parameter to be considered a non-deduced context
(14.8.2.5).
There's even an example right below
template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T
The problem is indeed deducibility, as mentioned by other answers. Instead of providing a second function taking an initializer_list, you may specify the type of the argument to foo when calling the function:
#include <initializer_list>
template<typename... Values>
void foo(Values...)
{
}
int main()
{
foo(1, 2, 3, "hello", 'a');
foo(std::initializer_list<int>{1}, std::initializer_list<int>{2, 3});
}
Deciding how to treat each parameter, is another question, however.
[EDIT]: Idea is taken from std::shared_ptr and initializer lists
This is bad. Consider a simple print() utility:
template <typename ...Args>
void print ( Args&&... args) ;
All of this would work:
print("Word", 12, 13.0f, true );
Tuple also works (ignore the implementation required):
auto tup = std::make_tuple("A", true, 42f ) ;
print("\nTuple I can pass it in ", tup );
But none of this works
print({1,2,3}); // spurious error messages
print({1}, {2}, {3}); // also
print("\nThe tuple: ", {12, 34, 56 } ) ; //also
The above "solution" does not help too:
template<typename ...Args>
inline void print(const std::initializer_list<Args>&... il_);
This (as above) does not give usable print() utility :
print("\nMy list is:\t", {1,2,3,4}) ; // error: function print() does not take 2 arguments?
Is it something obvious that is missing here? I would like to mix anything in the call to print() as it's declaration implies.
Anybody?
[Edit 2017-11-08]
Somebody has suggested
print("\nMy list is:\t", std::initializer_list<int>{1,2,3,4}) ;
And to somewhat remedy the pain of this, I am crushed to admit I have defined this macro "helper"
#define DBJ_IL(T,...) (std::initializer_list<T>{__VA_ARGS__})
Usage:
print("\nMy list is:\t", DBJ_IL(int,1,2,3,4)) ;
But alas, MSVC 14.11.25503 (the latest as of time of this writing) can not compile this. With errors coming from
1>c:\program files (x86)\microsoft visual
studio\2017\community\vc\tools\msvc\14.11.25503\include\utility(415):
error C2027: use of undefined type 'std::tuple_size<_Ty>'
1> with
1> [
1> _Ty=std::initializer_list<int>
1> ]
1>c:\program files (x86)\microsoft visual
studio\2017\community\vc\tools\msvc\14.11.25503\include\utility(415):
note: see declaration of 'std::tuple_size<_Ty>'
1> with
1> [
1> _Ty=std::initializer_list<int>
1> ]
1>c:\program files (x86)\microsoft visual
studio\2017\community\vc\tools\msvc\14.11.25503\include\tuple(1051):
note: see reference to variable template 'const ::size_t
tuple_size_v<std::initializer_list<int> >' being compiled
I am sure nobody want's the rest of the MSVC error dump ...
Is it me or is it them?
Doing the print() as generic lambda does not solve anything of course.
/*
forget templates
*/
namespace dbj { namespace {
auto print = [](auto... param)
{
if constexpr (sizeof...(param) > 0) {
char dummy[sizeof...(param)] = {
(( std::cout << param), 0)...
};
}
};
} }
Even if one passes single and simple init list this wont compile with the same error as above ...
dbj::print({1,2,3}) ; // msvc compilation error
I know C++17 type deduction of init lists is strengthened and improved, but I can not see in there anything to help me understand is this doable at all?
At last it seems it should be.