Подтвердить что ты не робот

Не удается передать несколько списков инициализаторов в шаблон вариационной функции

Я не понимаю сообщение об ошибке при попытке передать переменное количество списков инициализаторов:

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]

4b9b3361

Ответ 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 удержания списков инициализации усиливается и улучшается, но я не вижу в нем ничего, чтобы помочь мне понять, что это вообще возможно?

Наконец-то кажется, что это должно быть.