Как разрешить неоднозначный перегруженный вызов функции?

Когда я компилирую эту программу с помощью 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 вопроса:

  1. Это ошибка компилятора, которая компилируется, когда перегруженный метод сделан const?
  2. Как я могу гарантировать, что корректно перегруженная функция вызывается? (Нужно как-то привести аргумент?)

ТИА.

Скотт. 🙂

5

Решение

На самом деле 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 модификатор.

Итак, вы можете сделать:

  • явное приведение (как упомянуто в комментариях) … да, долго печатать …
  • объявить второй параметр foo w / template Func, В зависимости от того, что вы собираетесь делать, здесь есть несколько вариантов: его можно преобразовать в std::function по назначению (если вы хотите сохранить его какому-либо члену) или в случае немедленного вызова, вы даже получите некоторую оптимизацию (исключив преобразование в std::function)
  • более сложный способ — объявить обе функции с параметром шаблона и использовать std::enable_if превратить одного из них OFF в зависимости от std::is_same<bool, T> например (или проверить на вызываемый / тип функции)
  • использование тип диспетчеризации (да, снова с шаблонными функциями)

… Я думаю, этого достаточно 🙂

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]