Ответ 1
Проблема состоит в том, что как function<int()>
, так и function<int(int)>
являются конструктивными из одной и той же функции. Вот как выглядит объявление конструктора std::function
в VS2010:
template<class _Fx>
function(_Fx _Func, typename _Not_integral<!_Is_integral<_Fx>::value, int>::_Type = 0);
Игнорируя часть SFINAE, она конструктивна из всего, что угодно. std::/boost::function
используйте метод, называемый стиранием типа, чтобы позволить передавать суровые объекты/функции, так что они долго удовлетворяют сигнатуре при вызове. Один из недостатков заключается в том, что вы получаете ошибку в самой глубокой части реализации (где вызывается функция сохранения) при поставке объекта, который не может быть вызван, как требуется для его подписи, а не в конструкторе.
Проблема может быть проиллюстрирована этим небольшим классом:
template<class Signature>
class myfunc{
public:
template<class Func>
myfunc(Func a_func){
// ...
}
};
Теперь, когда компилятор ищет допустимые функции для набора перегрузки, он пытается преобразовать аргументы, если не существует идеальной функции фитинга. Преобразование может происходить через конструктор параметра функции или через оператор преобразования аргумента, заданного функции. В нашем случае это первое.
Компилятор пытается выполнить первую перегрузку a
. Чтобы сделать его жизнеспособным, он должен сделать преобразование. Чтобы преобразовать a int(*)()
в myfunc<int()>
, он пытается создать конструктор myfunc
. Являясь шаблоном, который принимает что-либо, преобразование, естественно, преуспевает.
Теперь он пытается сделать то же самое со второй перегрузкой. Конструктор все еще остается тем же и все еще берет что-либо, что ему дано, конверсия тоже работает.
Оставаясь с двумя функциями в наборе перегрузки, компилятор является грустным panda и не знает, что делать, поэтому он просто говорит, что вызов неоднозначен.
Итак, в конце часть Signature
шаблона относится к типу при создании объявлений/определений, но не в том случае, когда вы хотите построить объект.
Edit:
При всем моем внимании, отвечая на титульный вопрос, я полностью забыл о вашем втором вопросе.: (
Могу ли я обойти это или мне придется держать (раздражающие) явные приведения?
Afaik, у вас есть 3 варианта.
- Держите актерский состав
-
Сделайте объект
function
соответствующего типа и передайте егоfunction<int()> fx = x; function<int(int)> fy = y; a(fx); a(fy);
-
Скрыть утомительное кастинг в функции и использовать TMP для получения правильной подписи
Версия TMP (шаблон метапрограммирования) довольно многословна и имеет шаблонный код, но скрывает кастинг от клиента. Примерную версию можно найти здесь, которая основана на метафайле get_signature
, который частично специализируется на типах указателей функций (и дает хороший пример того, как сопоставление шаблонов может работать в С++):
template<class F>
struct get_signature;
template<class R>
struct get_signature<R(*)()>{
typedef R type();
};
template<class R, class A1>
struct get_signature<R(*)(A1)>{
typedef R type(A1);
};
Конечно, это нужно расширить для количества аргументов, которые вы хотите поддерживать, но это делается один раз, а затем зарывается в заголовок "get_signature.h"
.:)
Другим вариантом, который я рассматриваю, но немедленно отброшенным, была SFINAE, которая представила бы еще более шаблонный код, чем версия TMP.
Итак, да, это варианты, о которых я знаю. Надеюсь, один из них работает на вас.:)