Когда я компилирую эту программу с помощью gcc-4.6.3 или gcc-4.7.2, компилятор выдает мне ошибку о неоднозначности перегруженного вызова:
#include <iostream>
#include <functional>
class Scott
{
public:
void func(const bool b = true)
{
std::cout << "Called func() with a boolean arg" << std::endl;
}
void func(std::function<void(void)> f)
#ifdef WITH_CONST
const
#endif
{
std::cout << "Called func() with a std::function arg" << std::endl;
}
};int main (int argc, char *argv[])
{
Scott s;
s.func([] (void) { });
}
Однако, если я сделаю перегруженную функцию const, она прекрасно скомпилируется & вызывает метод, которого я не ожидал!
devaus120>> g++ -Wall -std=c++11 -DWITH_CONST wtf.cxx
devaus120>> ./a.out
Called func() with a boolean arg
Итак, у меня есть 2 вопроса:
ТИА.
Скотт. 🙂
На самом деле GCC это правильно! Потому что лямбда не функция но закрытие объекта типа класса! В самом деле! Вы даже можете наследовать от него 🙂 … даже несколько раз от разных лямбд …
Итак, согласно 8.5 / 16:
— Если тип назначения является (возможно, cv-квалифицированным) типом класса:
[…]— В противном случае, если исходный тип является (возможно, cv-квалифицированным) типом класса, рассматриваются функции преобразования. Перечислены применимые функции преобразования (13.3.1.5), и лучшая из них выбирается с помощью разрешения перегрузки (13.3). Выбранное пользователем преобразование, выбранное таким образом, вызывается для преобразования выражения инициализатора в инициализируемый объект. Если преобразование не может быть выполнено или является неоднозначным, инициализация неверна.
и 13.3.1.5:
При условиях, указанных в 8.5, в рамках инициализации объекта неклассного типа может быть вызвана функция преобразования для преобразования выражения инициализатора типа класса в тип инициализируемого объекта. Разрешение перегрузки используется для выбора функции преобразования, которая будет вызвана. Предполагая, что «cv1 T» — это тип инициализируемого объекта, а «cv S» — это тип выражения инициализатора, а S — тип класса, функции-кандидаты выбираются следующим образом:
— Рассматриваются функции преобразования S и его базовые классы. Те неявные функции преобразования, которые не скрыты в S и дают тип T или тип, который можно преобразовать в тип T с помощью стандартной последовательности преобразования (13.3.3.1.1), являются функциями-кандидатами. Для прямой инициализации те функции явного преобразования, которые не скрыты внутри S и дают тип T или тип, который может быть преобразован в тип T с квалификационным преобразованием (4.4), также являются функциями-кандидатами. Считается, что функции преобразования, которые возвращают cv-квалифицированный тип, дают cv-неквалифицированную версию этого типа для этого процесса выбора функций-кандидатов. Функции преобразования, которые возвращают «ссылку на cv2 X», возвращают lvalue или xvalues, в зависимости от типа ссылки, типа «cv2 X» и, следовательно, считаются выдающими X для этого процесса выбора функций-кандидатов.
наконец, результатом функции преобразования является указатель на функцию, которая неявно преобразуется в bool
…
Вы можете проверить эту серию конверсий с помощью следующего простого кода:
#include <iostream>
#include <iomanip>
int main()
{
std::cout << std::boolalpha << []{ return 0; } << '\n';
}
выход будет true
…
Вот несколько способов обойти … вам определенно нужно что-то, потому что обе функции подходят после разрешения перегрузки. Кстати, добавив const
, для подписи второго, просто исключите его, потому что у вас есть изменяемый экземпляр Scott
и снова вы получите ошибку компиляции, если объявите const
модификатор.
Итак, вы можете сделать:
Func
, В зависимости от того, что вы собираетесь делать, здесь есть несколько вариантов: его можно преобразовать в std::function
по назначению (если вы хотите сохранить его какому-либо члену) или в случае немедленного вызова, вы даже получите некоторую оптимизацию (исключив преобразование в std::function
)std::enable_if
превратить одного из них OFF в зависимости от std::is_same<bool, T>
например (или проверить на вызываемый / тип функции)… Я думаю, этого достаточно 🙂
Других решений пока нет …