2014-09-06 12 views
1

У нас есть функция, которая принимает объект функции как параметр. Есть две перегрузки функции, которые отличаются сигнатурой функции.Почему компилятор не может определить этот шаблон функции?

#include <functional> 

template <typename T> 
void foo(std::function<void(T)> bar) 
{ 

} 

template <typename T> 
void foo(std::function<void(int, T)> bar) 
{ 

} 

int main() 
{ 
    foo([](float number) { 

    }); 
    return 0; 
} 

Однако этот код не компилируется.

error C2784: 'void foo(std::function<void(int,T)>)' : could not deduce template argument for 'std::function<void(int,T)>' from 'main::<lambda_2b4e4413ec419a4ac179a0b64ebde221>' : see declaration of 'foo' 
error C2784: 'void foo(std::function<void(T)>)' : could not deduce template argument for 'std::function<void(T)>' from 'main::<lambda_2b4e4413ec419a4ac179a0b64ebde221>' : see declaration of 'foo' 

Я думаю, что проблема как с самим шаблоном, так и с перегрузкой. Как я могу предоставить две функции, соответствующие указанным выше подписям?

+0

лямбда не является производным от станд :: функция – 4pie0

+1

Тип шаблона дедукция пытается найти типы для 'Т *, так что' станд :: функция <аннулируются (интермедиат , T)> 'или' std :: function 'соответственно, становится равным * типу аргумента' [] (float) {} '. Нет такого 'T', что делает тип лямбда равным некоторому типу' std :: function <..> '. – dyp

ответ

3

Lambdas in C++ - это экземпляры анонимного класса, специально созданного для каждого лямбда-объекта, это означает, что два лямбда с таким же кодом фактически являются различными объектами.

Это также означает, что вы не можете пропускать лямбды без использования шаблона, потому что нет лямбда-типа. Lambda не является std::function (и не является его производным), а не указателем функции. Это уникальные типы, реализующие оператор приложения, т. Е. operator().

Вы можете сделать это с помощью static_cast оператор

template <typename T> 
void foo(std::function<void(T)> bar) 
{ 
} 

foo(static_cast< std::function<void(float)> >([](float number){})); 
//or 
foo(std::function<void(float)>{ [](float){} }); 
+1

'static_cast', btw, может даже быть опущен:' foo (std :: function {[] (float) {}}); 'но это не отвечает на вопрос OP, я думаю. – dyp

+0

ну, как только это правда, еще лучше использовать static_cast, чтобы сигнализировать о резкости приведения – 4pie0

1

Причина, по-видимому один из побочных эффектов того, что каждый лямбда-выражения, чтобы быть иметь свой собственный тип.

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

Решение заключается в том, чтобы как можно скорее обернуть лямбда в объекты std::function<...>, поскольку они имеют тип, который можно указать и сопоставить в шаблонах.

Например, у вас не может быть функция, которая возвращает лямбда, или хранить лямбда в члене ... но вы можете вернуть std::function или сохранить std::function в члены.

В конкретных код работает с

foo(std::function<void(float)>([](float number){}); 
+1

* «Это также означает, что вы не можете использовать lambdas, когда требуется дедукция типа» * Слишком общий. Вы не можете выводить возвращаемый тип лямбда непосредственно через «сопоставление образцов» и т. Д. Вы можете делать то, что хочет OP через SFINAE. * «Например, у вас не может быть функции, которая возвращает лямбда». Теперь у нас есть C++ 14, с выводом типа возврата для обычных функций. * "или сохранить лямбда в члене." * Это всегда возможно, выведя тип лямбда и используя шаблон класса. То, что вы * не можете сделать, (ИМХО, если быть более точным), - это * имя * тип лямбда напрямую. – dyp