#c #templates
#c #шаблоны
Вопрос:
Я определяю метод следующим образом:
template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
func(arg);
}
и использовать его так — например—:
foo(2, [](int i) -> void { cout << i << endl; });
Почему компилятор не может определить тип, поскольку это определенно int
?
Я получаю 'void foo(ArgT,boost::function<void(ArgT)>)' : could not deduce template argument for 'boost::function<void(ArgT)>' from 'anonymous-namespace'::<lambda0>'
.
Ответ №1:
Хотя лямбды C строго мономорфны, они являются просто сокращением для объектов функций (иначе функторов), и в общем случае функторы могут быть полиморфными; т. Е. их операторы вызова могут быть перегружены или шаблонны. В результате функторы (и, следовательно, лямбды) никогда не преобразуются неявно в шаблонные std::function<>
(или boost::function<>
) экземпляры, потому что operator()
типы аргументов функторов не выводятся автоматически.
Выражаясь несколько иначе, естественным типом вашего лямбда-выражения является функтор с конструктором без параметров и operator()
с сигнатурой void operator ()(int) const
. Каким бы очевидным этот факт ни был для нас с вами, он не является автоматически выводимым, который ArgT
должен разрешаться в int
, потому что лямбды являются функторами, а operator()
функторы можно перегружать и шаблонизировать.
TL; DR: То, что вы хотите, невозможно.
Комментарии:
1. я думаю, это действительно прискорбно. Будем надеяться, что это ограничение можно снять для следующей итерации.
2. Я попытался вызвать
foo()
сbar()
помощью функции, избегающей лямбда-выражения: тот же результат.
Ответ №2:
Вы хотите преобразование из лямбда-функции в то, boost::function<void(ArgT)>
где ArgT
должно быть выведено. Как правило, у вас не может быть вывода типа и преобразования в одном и том же аргументе функции: при выводе параметра шаблона преобразования не выполняются.
Причина этого заключается в следующем. Здесь задействованы три типа: (1) параметр шаблона, (2) тип параметра функции, (3) тип передаваемого объекта. Два типа (1 и 2) могут быть выведены друг из друга, но оба неизвестны. Если компилятор может предположить, что 2 и 3 имеют один и тот же тип, проблема решена, но если все, что компилятор знает, это то, что 3 можно преобразовать в 2, может быть любое количество возможных решений, и ожидается, что компилятор не решит проблему. На практике мы знаем, что в данном конкретном случае существует только одно возможное решение, но стандарт не делает различия между случаями.
Приведенное выше правило применяется во всех выводимых контекстах, даже если параметр шаблона может быть выведен из другого параметра функции. Решение здесь заключается в том, чтобы сделать соответствующий параметр функции невыводимым контекстом, то есть контекстом, в котором компилятор никогда не будет пытаться вывести параметр шаблона из параметра функции. Это можно сделать следующим образом:
template <class T> struct identity { typename T type; };
template <class ArgT>
void foo(ArgT arg, typename identity<::boost::function<void(ArgT)>>::type func)
{
func(arg);
}