Перегрузка на std :: function <...> - PullRequest
12 голосов
/ 19 января 2011

С учетом следующего кода: -

#include <algorithm>
#include <iostream>
#include <functional>
#include <string>

void func(std::function<void(void)> param)
{
    param();
}

void func(std::function<void(int)> param)
{
    param(5);
}

int main(int argc, char* argv[])
{
    func([] () { std::cout << "void(void)" << std::endl; });
    func([] (int i) { std::cout << "void(int): " << i << std::endl; });

    std::string line;
    std::getline(std::cin, line);
    return 0;
}

Ошибка компиляции из VS2010: -

CppTest.cpp(18): error C2668: 'func' : ambiguous call to overloaded function
1>          CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (int)
1>          ]
1>          CppTest.cpp(6): or       'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (void)
1>          ]
1>          while trying to match the argument list '(`anonymous-namespace'::<lambda0>)'
1>CppTest.cpp(19): error C2668: 'func' : ambiguous call to overloaded function
1>          CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (int)
1>          ]
1>          CppTest.cpp(6): or       'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (void)
1>          ]
1>          while trying to match the argument list '(`anonymous-namespace'::<lambda1>)'

Ошибка компиляции из g ++ - 4,5

program2.cpp: In function ‘int main(int, char**)’:
program2.cpp:18:68: error: call of overloaded ‘func(main(int, char**)::<lambda()>)’ is ambiguous
program2.cpp:6:10: note: candidates are: void func(std::function<void()>)
program2.cpp:11:10: note:                 void func(std::function<void(int)>)
program2.cpp:19:79: error: call of overloaded ‘func(main(int, char**)::<lambda(int)>)’ is ambiguous
program2.cpp:6:10: note: candidates are: void func(std::function<void()>)
program2.cpp:11:10: note:                 void func(std::function<void(int)>)

Так что, похоже, компилятор не может понять, что лямбда [] () -> void может быть назначена только std :: function и лямбда [] (int) -> void может быть назначен только std :: function . Это должно произойти или просто недостаток в компиляторах?

1 Ответ

13 голосов
/ 19 января 2011

Это должно произойти или просто недостаток в компиляторах?

Это должно произойти.std::function имеет шаблон конструктора, который может принимать аргументы любого типа.Компилятор не может знать до тех пор, пока шаблон конструктора не будет выбран и создан, что он может столкнуться с ошибками, и он должен иметь возможность выбрать перегрузку вашей функции, прежде чем он сможет это сделать.

НаиболееПростое решение - использовать приведение или явно создать объект std::function правильного типа:

func(std::function<void()>([](){}));
func(std::function<void(int)>([](int){}));

Если у вас есть компилятор, который поддерживает преобразование захвата-лямбды-функции-указателя и вашЛямбда ничего не захватывает, вы можете использовать необработанные указатели функций:

void func(void (*param)()) { }
void func(void (*param)(int)) { }

(Похоже, вы используете Visual C ++ 2010, который не поддерживает это преобразование. Преобразование не было добавлено в спецификацию донезадолго до поставки Visual Studio 2010, слишком поздно, чтобы добавить его.)


Чтобы объяснить проблему более подробно, рассмотрим следующее:

template <typename T>
struct function {

    template <typename U>
    function(U f) { }
};

Это в основномкак выглядит рассматриваемый конструктор std::function: Вы можете вызывать его с любым аргументом, даже если аргумент не имеет смысламожет вызвать ошибку где-то еще.Например, function<int()> f(42); будет вызывать этот шаблон конструктора с U = int.

В вашем конкретном примере компилятор находит две функции-кандидата во время разрешения перегрузки:

void func(std::function<void(void)>)
void func(std::function<void(int)>)

Тип аргумента,какое-то необъяснимое имя лямбда-типа, которое мы будем называть F, не соответствует ни одному из них в точности, поэтому компилятор начинает смотреть, какие преобразования он может сделать в F, чтобы попытаться привести его в соответствие с одной из этих функций-кандидатов,При поиске конверсий он находит вышеупомянутый шаблон конструктора.

Все, что компилятор видит в этой точке, это то, что он может вызывать любую функцию, потому что

  • он может преобразовать F в std::function<void(void)>, используя конструктор преобразования с U = F и
  • он может конвертировать F в std::function<void(int)>, используя конструктор преобразования с U = F.

В вашем примере очевидно, что только один из них будет выполнен без ошибок, нов общем случае это не так.Компилятор не может ничего сделать дальше.Он должен сообщить о неоднозначности и потерпеть неудачу.Он не может выбрать одно, потому что оба преобразования одинаково хороши, и ни одна перегрузка не лучше, чем другое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...