Привет, простите, если простой вопрос, я тут с некоторым отставанием пытаюсь освоить с++17...
Вот такой код
template<class F>
void test(F f) {
f(1, 2);
}
template<class T>
auto func(T a, T b) {
return a + b;
}
int main() {
test([](auto x, auto y) { func(x, y);}); // компилируется
test(func); // не компилируется
}
gcc выдаёт
test.cpp: In function ‘int main()’:
test.cpp:15:14: error: no matching function for call to ‘test(<unresolved overloaded function type>)’
15 | test(func);
| ^
test.cpp:2:6: note: candidate: ‘template<class F> void test(F)’
2 | void test(F f) {
| ^~~~
test.cpp:2:6: note: template argument deduction/substitution failed:
test.cpp:15:14: note: couldn’t deduce template parameter ‘F’
15 | test(func);
| ^
Если func сделать простой функцией, без шаблона, всё работает. С шаблоном только через обёртку из лямбды.
Так и должно быть? Можно как-то просто объяснить, почему?
Здравствуйте, reversecode, Вы писали:
R>а как она выведет что у вас там под шаблоном?
R>test(func<int>);
Ну аргументы-auto у лямбды его же не смущают, как-то он подходящий тип определяет...
Если явно указать, конечно, работает. Но это же не очень интересные случаи, когда получается явно и коротко указать тип.
я не помню как там в стандарте это правильно называется и какой там пункт
но у лямбды есть сигнатура
а у шаблона нет
поэтому сделав первый проход
компилятор не находит нужной функции
как то так
есть на ютубе у Константина Владимирова
лекции по с++
там есть подобный случай
и подробное объяснение
Здравствуйте, reversecode, Вы писали:
R>я не помню как там в стандарте это правильно называется и какой там пункт R>но у лямбды есть сигнатура R>а у шаблона нет R>поэтому сделав первый проход R>компилятор не находит нужной функции R>как то так
Понял, то есть для лямбды есть какой-то встроенный хак, ладно.
R>есть на ютубе у Константина Владимирова R>лекции по с++ R>там есть подобный случай R>и подробное объяснение
О, спасибо, толковых лекций мне сильно не хватало, посмотрю.
Это поведение существует и требуется с первого стандарта С++. Лямбда к нему ничего нового не добавляет. Эквивалентный код на шаблонах (в который компилятор превращает лямбду) описывается этой первой версией языка и ведёт себя так же (компилируется или нет в двух этих вызовах).
Здравствуйте, SergH, Вы писали:
SH>Так и должно быть? Можно как-то просто объяснить, почему?
Шаблонная лямбда порождает нешаблонный класс с шаблонным методом `operator ()`. Ключевой момент тут в том, что тип лямбды, т.е. самого closure-объекта — не шаблонный. Таким образом при вызове функции
test([](auto x, auto y) { func(x, y);});
никаких трудностей с дедукций шаблонного параметра `F` у компилятора не возникает — он сразу определен однозначно. Дедукция `auto` для вашей лямбды будет делаться позже совсем в другом месте — в точке вызова `operator ()`, то есть в точке `f(1, 2);`, где все тоже однозначно.
Для варианта
test(func);
ситуация совсем иная — дедукция всех шаблонных аргументов (и `F` для `func` и `T` для `test`) должна быть сделана немедленно в этой точке. А это невозможно.
Здравствуйте, SergH, Вы писали:
SH>Привет, простите, если простой вопрос, я тут с некоторым отставанием пытаюсь освоить с++17...
SH>Вот такой код
SH>
SH>template<class F>
SH>void test(F f) {
SH> f(1, 2);
SH>}
SH>template<class T>
SH>auto func(T a, T b) {
SH> return a + b;
SH>}
SH>int main() {
SH> test([](auto x, auto y) { func(x, y);}); // компилируется
SH> test(func); // не компилируется
SH>}
SH>
SH>gcc выдаёт
SH>
SH>test.cpp: In function ‘int main()’:
SH>test.cpp:15:14: error: no matching function for call to ‘test(<unresolved overloaded function type>)’
SH> 15 | test(func);
SH> | ^
SH>test.cpp:2:6: note: candidate: ‘template<class F> void test(F)’
SH> 2 | void test(F f) {
SH> | ^~~~
SH>test.cpp:2:6: note: template argument deduction/substitution failed:
SH>test.cpp:15:14: note: couldn’t deduce template parameter ‘F’
SH> 15 | test(func);
SH> | ^
SH>
SH>Если func сделать простой функцией, без шаблона, всё работает. С шаблоном только через обёртку из лямбды.
SH>Так и должно быть? Можно как-то просто объяснить, почему?
Параметр функции test() шаблонный, следовательно компилятор не может определить адрес какой перегрузки функции func() ты хочешь передать.
Это можно указать явно:
template<class F>
void test(F f) {
f(1, 2);
}
template<class T>
auto func(T a, T b) {
return a + b;
}
int main() {
test([](auto x, auto y) { func(x, y);}); // компилируется
test( static_cast<decltype(func(1, 2)) (*)(int, int)>(func) ); // такая лапша
}
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Шаблонная лямбда порождает нешаблонный класс с шаблонным методом `operator ()`. Ключевой момент тут в том, что тип лямбды, т.е. самого closure-объекта — не шаблонный.
Спасибо, это всё объясняет! Я думал, он для auto заглядывает внутрь и выясняет, с каким типом её делать, и потому удивлялся, что он не может сделать то же самое и для шаблона. О таком простом и красивом способе не подумал.
Здравствуйте, SergH, Вы писали:
SH>Спасибо, это всё объясняет! Я думал, он для auto заглядывает внутрь и выясняет, с каким типом её делать, и потому удивлялся, что он не может сделать то же самое и для шаблона. О таком простом и красивом способе не подумал.
О! И поэтому же работает даже вот такое вот:
#include <string>
#include <iostream>
using namespace std;
template<class F>
void test(F f) {
cout << f(1, 2) << "\n"; // складывает как числа
cout << f("12"s, "34"s) << "\n"; // складывает как строки
}
int main() {
test([](auto x, auto y) { return x + y;});
}