Почему g cc выводит тип из последнего аргумента функции обратного вызова здесь? - PullRequest
2 голосов
/ 08 февраля 2020

Я пытаюсь создать шаблонную функцию, которая получает указатель на функцию с любым количеством входов, за исключением того, что она должна иметь int в качестве последнего ввода. Затем функция шаблона должна вызывать эту функцию с предоставленными аргументами и пятью в качестве последнего аргумента. Код прояснит это:

#include <iostream>

template<class A, class ... B>
void passFive(A (*f)(B ..., int n), B ... x) {
        f(x ..., 5);
}

void printStrAndInt(const char *s, int n) {
        std::cout << s << " " << n << "\n";
}

int main() {
        passFive(&printStrAndInt, "pineapple");
        return 0;
}

Однако g cc это не нравится и выдает ошибку вместе с примечаниями:

test.cpp: In function ‘int main()’:
test.cpp:14:39: error: no matching function for call to ‘passFive(void (*)(const char*, int), const char [10])’
  passFive(&printStrAndInt, "pineapple");
                                       ^
test.cpp:4:6: note: candidate: template<class A, class ... B> void passFive(A (*)(B ..., int), B ...)
 void passFive(A (*f)(B ..., int n), B ... x) {
      ^~~~~~~~
test.cpp:4:6: note:   template argument deduction/substitution failed:
test.cpp:14:39: note:   mismatched types ‘int’ and ‘const char*’
  passFive(&printStrAndInt, "pineapple");

Таким образом, это выводит тип для B один раз как const char * (как и ожидалось), но также один раз как второй аргумент printStrAndInt (я уверен в этом, потому что то же самое с другими типами данных, кроме int). Как примечание стороны, это прекрасно работает, если я перемещаю int вперед, как это:

#include <iostream>

template<class A, class ... B>
void passFive(A (*f)(int n, B ...), B ... x) {
        f(5, x ...);
}

void printStrAndInt(int n, const char *s) {
        std::cout << s << " " << n << "\n";
}

int main() {
        passFive(&printStrAndInt, "pineapple");
        return 0;
}

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

1 Ответ

1 голос
/ 09 февраля 2020

Проблема: переменная c список параметров шаблона может быть выведен только тогда, когда он находится в последней позиции.

Также учитывается, что, когда он не находится в последней позиции, выводится пустым.

Итак, из

template<class A, class ... B>
void passFive(A (*f)(B ..., int n), B ... x) {
        f(x ..., 5);
}

и вызова

passFive(&printStrAndInt, "pineapple");

список variadi c B... должен выводиться из f подпись как empy (B... не в последней позиции, затем следуют int) и char const * (или, может быть, char const [10]) из "pineapple".

Чтобы избежать этого, вы можете вывести два разных списка и наложить (SFINAE или static_assert()), что второй список variadi c является первым, добавив int.

Я имею в виду (путь SFINAE) что-то как

template <typename A, typename ... Bs, typename ... Cs>
std::enable_if_t<std::is_same_v<std::tuple<Bs...>,
                                std::tuple<Cs..., int>>>
   passFive(A (*f)(Bs ...), Cs ... x)
 { f(x ..., 5); }

Не идеальное решение: как и в исходном коде, проблема в том, что функция f ожидает (например) a long и в аргументах x... вы передаете int.

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

...