Не удается передать несколько списков инициализаторов в шаблон вариационной функции
Я не понимаю сообщение об ошибке при попытке передать переменное количество списков инициализаторов:
template<typename... Values>
void foo(Values...)
{
}
int main()
{
foo(1, 2, 3, "hello", 'a'); // OK
foo({1}, {2, 3}); // ERROR
}
Сообщение об ошибке жалуется на слишком много аргументов:
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...)
^
Однако, если я не смогу передать столько аргументов, сколько захочу? [ссылка ideone]
Ответы
Ответ 1
Проблема, вероятно, выводимость. {}
могут быть равномерными инициализаторами для любого из аргументов.
Это работает:
#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});
}
Смотрите Live on Coliru
Ответ 2
Проблема заключается не в аргументах varadic, а в том, что компилятор не может вывести тип вложенного списка инициализаторов, кроме случая, когда вы объявляете параметр std::initializer_list<T>
§ 14.8.2.1 Вывод аргумента шаблона производится путем сравнения каждой функции тип шаблона шаблона (назовите его P) с типом соответствующего аргумент вызова (назовите его A), как описано ниже. При удалении ссылки и cv-квалификаторы из P задают std:: initializer_list для некоторого P0, а аргумент - это список инициализаторов (8.5.4), то вычет выполняется для каждого элемента инициализатора список, взяв P0 как тип параметра шаблона функции и инициализатор в качестве аргумента. В противном случае список инициализаторов аргумент заставляет параметр считаться не выводимым контекстом(14.8.2.5).
Там даже пример ниже
template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T
Ответ 3
Проблема заключается в действительно выводимости, о чем говорят другие ответы. Вместо предоставления второй функции, содержащей список initializer_list, вы можете указать тип аргумента foo при вызове функции:
#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});
}
Решение о том, как обрабатывать каждый параметр, является другим вопросом.
[EDIT]: Идея взята из std:: shared_ptr и списков инициализации
Ответ 4
Это плохо. Рассмотрим простую утилиту print():
template <typename ...Args>
void print ( Args&&... args) ;
Все это сработает:
print("Word", 12, 13.0f, true );
Tuple также работает (игнорируйте требуемую реализацию):
auto tup = std::make_tuple("A", true, 42f ) ;
print("\nTuple I can pass it in ", tup );
Но ничего из этого не работает
print({1,2,3}); // spurious error messages
print({1}, {2}, {3}); // also
print("\nThe tuple: ", {12, 34, 56 } ) ; //also
Вышеупомянутое "решение" тоже не помогает:
template<typename ...Args>
inline void print(const std::initializer_list<Args>&... il_);
Это (как указано выше) не дает полезной утилиты print():
print("\nMy list is:\t", {1,2,3,4}) ; // error: function print() does not take 2 arguments?
Это что-то очевидное, что здесь отсутствует? Я хотел бы смешать что-нибудь в вызове print(), как следует из заявления.
Кто-нибудь?
[Редактировать 2017-11-08]
Кто-то предложил
print("\nMy list is:\t", std::initializer_list<int>{1,2,3,4}) ;
И чтобы немного исправить эту боль, я раздавлен, чтобы признать, что я определил этот макрос "помощник"
#define DBJ_IL(T,...) (std::initializer_list<T>{__VA_ARGS__})
Использование:
print("\nMy list is:\t", DBJ_IL(int,1,2,3,4)) ;
Но, увы, MSVC 14.11.25503 (последнее по времени написания) не может скомпилировать это. С ошибками, исходящими из
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
Я уверен, что никто не хочет остальную ошибку MSVC error...
Это я или это они?
Выполнение print() в качестве общей лямбды ничего не решает, конечно.
/*
forget templates
*/
namespace dbj { namespace {
auto print = [](auto... param)
{
if constexpr (sizeof...(param) > 0) {
char dummy[sizeof...(param)] = {
(( std::cout << param), 0)...
};
}
};
} }
Даже если один проходит один и простой список инициализации, это не будет компилироваться с той же ошибкой, что и выше...
dbj::print({1,2,3}) ; // msvc compilation error
Я знаю, что тип С++ 17 удержания списков инициализации усиливается и улучшается, но я не вижу в нем ничего, чтобы помочь мне понять, что это вообще возможно?
Наконец-то кажется, что это должно быть.