У меня есть следующий код, который не может быть скомпилирован
template <typename T>
void call_with(std::function<void(T)> f, T val) {
f(val);
}
int main() {
auto print = [](int x) { std::cout << x; };
call_with(print, 42);
}
И ошибка компиляции выглядит так
tst.cpp:17:2: error: no matching function for call to 'call_with'
call_with(print, 42);
^~~~~~~~~
tst.cpp:11:6: note: candidate template ignored: could not match 'function<void (type-parameter-0-0)>' against
'(lambda at tst.cpp:16:15)'
void call_with(std::function<void(T)> f, T val) {
^
1 error generated
Я попытался скомпилировать его с помощью g++ tst.cpp -o tst -std=c++17
Итак, я знаю, что иногда C++ может выводить аргументы шаблона при некоторых условиях, но мне интересно, почему в этом случае он не может скомпилировать этот код. Похоже, что не может быть никаких других вариантов, чтобы тип T
был чем угодно, кроме int, и чтобы f
был чем угодно, кроме std::function<void(int)>
.
std::function<void(double)>
можно построить из[](int){}
. То, что лямбда принимает параметр типаT
, не означает, чтоstd::function
должен принимать параметр того же типа. - person Igor Tandetnik   schedule 29.11.2020T val
, он будет корректно выведен какint
, хотя42
можно рассматривать какdouble
. Итак, почему существует неоднозначность сstd::function
и нет неоднозначности с базовым аргументом шаблонаT
? - person Michael Solotky   schedule 29.11.2020class L
(какой-то безымянный тип класса, синтезированный для лямбда-выражения) не соответствуетstd::function<void(T)>
- person Igor Tandetnik   schedule 29.11.202042
нельзя рассматривать как двойник.42
в качестве выражения представляет собой целое число, которое может соответствоватьT
. Это бетон.std::function
стирает тип вызываемого (любого вызываемого) и делает это путем преобразования. Шаблоны никогда не будут учитывать конверсии при выводе вещей по своему замыслу. - person StoryTeller - Unslander Monica   schedule 29.11.2020std::function<void(T)>
есть конструкторtemplate< class F > function( F f );
— на первый взгляд, он может принимать что угодно. У компилятора нет возможности узнать, что он должен искать в аргументе все способы применения к нему оператора вызова функции, а затем сопоставлять с образцом типы параметров такого вызова функции — это часть семантикиstd::function
это никак не отражено в синтаксисе. - person Igor Tandetnik   schedule 29.11.2020T
в невыведенный контекст, например. какtemplate <typename T> void call_with(std::function<void(std::type_identity_t<T>)> f, T val)
(type_identity_t
— это функция C++20, но эквивалент можно тривиально вручную свернуть для более ранних версий). Таким образом,T
будет однозначно выводиться из второго аргумента. - person Igor Tandetnik   schedule 29.11.2020std::function
он этого не делает. - person Michael Solotky   schedule 29.11.2020call_with<int>(print, 42);
должен решить проблему. - person macroland   schedule 29.11.2020