Варадические шаблоны не компилируют компилятор MSVE? - PullRequest
0 голосов
/ 05 сентября 2018

Попытка скомпилировать следующий фрагмент кода: Этот код явно использует функцию C ++ 11 и описан в книге «Язык программирования C ++» §3.4.4

template<typename T>
void g(T x)
{
    std::cout << x << " ";
}

template<typename T, typename... Tail>
void f(T head, Tail... tail) {
    g(head); // do something to head
    f(tail...); // tr y again with tail
}

void f() {}

int main()
{
    f(1, "Lol", 5);
    getchar();
}

VS17 выход:

C2672 'f': не найдена соответствующая перегруженная функция Строка: 21

'void f (T, Tail ...)': ожидается 2 аргумента - 0 при условии Строка: 19

есть идеи?

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Здесь три вещи неверны.

  1. tail имя шаблона тени аргумента tail
  2. Нет жизнеспособной функции для обработки базового случая рекурсивного вызова f()
  3. f() должен быть определен раньше других f(...), поскольку в таких рекурсивных функциях расширенный поиск функций не выполняется.

* Решения 1017 *

Измените имя аргумента tail на другое, например

template<typename T, typename... tail>
void f(T head, tail... ftail) { //tail here was shadowing actual template name tail so changed to ftail
    g(head); // do something to head
    f(ftail...); // try again with tail
}

Когда вы делаете рекурсивный вызов f(), наступает момент, когда значение не передается f(..), потому что каждый рекурсивный вызов уменьшает число передаваемых аргументов на 1.

Допустим, вы начали с f(1, "Lol", 2), внутренне рекурсивный вызов сделан на f("Lol", 2), этот inturns делает вызов f(2), который вызывает f(), но ваша функция f ожидает как минимум 1 параметр. Отсюда ошибка

Чтобы решить эту проблему, просто перегрузите сценарий f для базового варианта без аргументов, таких как

void f(){
    //last recursive call made
}

Вот полный код

#include<iostream>

template<typename T>
void g(T x)
{
    std::cout << x << " ";
}

void f(){ }

template<typename T, typename... tail>
void f(T head, tail... ftail) {
    g(head); // do something to head
    f(ftail...); // try again with tail
}

int main()
{
    f(1, "Lol", 5);
    getchar();
}
0 голосов
/ 05 сентября 2018

Первый MCVE:

template<typename T, typename... Tail>
void f(T head, Tail... tail) {
    f(tail...); // tr y again with tail
}
f(1, 2, 3);

Теперь создайте экземпляр:

f<int, int, int>(1, 2, 3);

компилируется в:

template<T = int, Tail...={int,int}>
void f(int head, int tail0, int tail1) {
    f(tail0, tail1); // tr y again with tail
}

Рекурсивный вызов:

    f<int,int>(tail0, tail1); // tr y again with tail

, который компилируется в:

template<T = int, Tail...={int}>
void f(int head, int tail0) {
    f(tail0); // tr y again with tail
}

рекурсивный вызов разрешается в:

    f<int>(tail0); // tr y again with tail

, который компилируется в:

template<T = int, Tail...={}>
void f(int head) {
    f(); // tr y again with tail
}

и здесь мы пытаемся позвонить f().

Нет действительного вызова f() видимым, поэтому вы получаете сообщение об ошибке.

A void f() {} в вызов f() здесь не помогает, потому что в шаблоне поиск выполняется до того, как f() станет видимым.

Если вы хотите исправить это простым способом, вы можете добавить inline void f(){} над шаблоном f.

Чем сложнее путь?

template<class...Ts>
void f(Ts...ts) {
  using discard=int[];
  (void)discard{ 0, ( void(
    g(ts)
  ),0)...};
}

, который также устраняет рекурсию. Или в :

template<class...Ts>
void f(Ts...ts) {
  ( (void)(g(ts)), ... );
}

это также быстрее компилируется, так как создает намного меньше (и короче) символов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...