Ошибка замены с `std::function` и ранее выведенным параметром шаблона - почему?
Рассмотрим следующий код:
template <typename>
struct S { };
void g(S<int> t);
template <typename T>
void f(T, std::function<void(S<T>)>);
При попытке вызвать
f(0, g);
Я получаю следующую ошибку:
error: no matching function for call to 'f' f(0, g); ^ note: candidate template ignored: could not match 'function<void (S<type-parameter-0-0>)>' against 'void (*)(S<int>)' void f(T, std::function<void(S<T>)>); ^
Хотя я понимаю, что вообще тип std::function
параметр не может быть выведен, так как это невыведенный контекст
В этом случае T
можно сначала вывести по переданному аргументу 0
, а затем заменен на std::function<void(S<T>)>
получить std::function<void(S<int>)>
.
Я ожидал, что после вывода T=int
, компилятор заменит T
везде в подписи, а затем попытайтесь построить std::function
параметр с аргументом g
.
Почему это не так? Я предполагаю, что порядок, в котором происходит замена / вычитание, как-то связан с этим, но я бы хотел увидеть соответствующую формулировку Стандарта.
Дополнительный вопрос: это то, что потенциально может быть изменено в будущем Стандарте с сохранением обратной совместимости, или есть фундаментальная причина, по которой такая замена не работает?
2 ответа
Хотя я понимаю, что обычно тип параметра std::function не может быть выведен, поскольку это невыведенный контекст.
Это не невыведенный контекст. Наоборот. Поскольку вычет по параметруstd::function
предпринимается попытка, но аргумент не являетсяstd::function
, удержание не выполняется. Выведение аргументов шаблона из аргументов функции должно согласовываться для всех аргументов функции. Если что-то не удастся, то потерпит неудачу полностью.
[temp.deduct.type]
2 В некоторых случаях выведение выполняется с использованием единственного набора типов P и A, в других случаях будет набор соответствующих типов P и A. Выведение типа выполняется независимо для каждой пары P/A, и выводится значения аргументов шаблона затем объединяются. Если вывод типа не может быть выполнен для какой-либо пары P/A, или если для любой пары вывод приводит к более чем одному возможному набору выводимых значений, или если разные пары дают разные выведенные значения, или если какой-либо аргумент шаблона не остается ни выведенным, ни явным указано, вывести аргумент шаблона не удается.
Превращение типа второго параметра функции в невыведенный контекст - вот как можно преодолеть ошибку.
#include <functional>
template<typename T>
struct type_identity {
using type = T;
};
template <typename>
struct S { };
void g(S<int> ) {}
template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}
int main() {
f(0, g);
}
T
успешно выводится из первого аргумента функции, и выводить нечего. Таким образом, посвящение считается успешным.
Хотя я понимаю, что вообще тип
std::function
параметр не может быть выведен, поскольку это невыведенный контекст, в данном случаеT
можно сначала вывести по переданному аргументу0
.
Это неправда. T
выводится в этом контексте. Если вы измените код на
template <typename T>
void f(std::function<void(S<T>)>);
int main()
{
f(std::function<void(S<int>)>(g));
}
код будет компилироваться и T
правильно выводится.
Ваша проблема в том, что вы передаете объект функции, который она не может извлечь T
от. Компилятор не будет преобразовывать аргументы функции, когда он пытается вывестиT
. Это означает, что у вас естьint
и функция как типы, переданные в функцию. Он получаетint
от 0
, затем пытается получить тип из std::function
вы передаете второй параметр, но поскольку вы не прошли std::function
он не может извлечь T
и из-за этого вы получите ошибку.