Почему мой компилятор C ++ не может определить аргумент шаблона для функции boost? - PullRequest
9 голосов
/ 03 мая 2011

Я определяю метод следующим образом:

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>'.

Ответы [ 2 ]

10 голосов
/ 03 мая 2011

В то время как лямбды C ++ строго мономорфны, они являются просто сокращением для функциональных объектов (или функторов), и в общем случае функторы могут быть полиморфными; то есть их операторы вызовов могут быть перегружены или шаблонизированы. В результате функторы (и, следовательно, лямбда-выражения) никогда неявно преобразуются в шаблонные std::function<> (или boost::function<>) экземпляры, поскольку типы аргументов функторов operator() не могут автоматически выводиться.

Чтобы сформулировать это несколько иначе, естественным типом вашего лямбда-выражения является функтор с конструктором без параметров и operator() с подписью void operator ()(int) const. Как бы ни был очевиден этот факт для вас и меня, автоматически не следует, что ArgT должен разрешаться до int, потому что лямбды - это функторы, а функторы operator() можно перегружать и шаблонировать.

TL; DR: То, что вы хотите, невозможно.

8 голосов
/ 03 мая 2011

Требуется преобразование лямбда-функции в 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);
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...