Устранение неоднозначности вызовов функций, принимающих std :: functions - PullRequest
5 голосов
/ 06 ноября 2010

Код ниже не компилируется в gcc 4.5, потому что вызов foo неоднозначен.Как правильно это исправить?

#include <iostream>
#include <functional>
using namespace std;

void foo(std::function<void(int, int)> t)
{
    t(1, 2);
}

void foo(std::function<void(int)> t)
{
    t(2);
}

int main()
{
    foo([](int a, int b){ cout << "a: " << a << " b: " << b << endl;});
}

Ответы [ 2 ]

6 голосов
/ 06 ноября 2010

Лучший способ - явно создать объект std::function правильного типа, а затем передать этот объект в функцию:

std::function<void(int, int)> func = 
    [](int a, int b) { cout << "a: " << a << " b: " << b << endl; }
foo(func);

или встроенный:

foo(
    std::function<void(int, int)>(
        [](int a, int b) { cout << "a: " << a << "b: " << b << endl; }
));

std::function имеет шаблон конструктора, который принимает все:

template<class F> function(F);

Из-за этого у компилятора нет возможности узнать во время разрешения перегрузки, что foo можно выбрать: и std::function<void(int)>, и std::function<void(int, int)> имеют конструктор, который может принимать ваше лямбда-выражение в качестве аргумента.

При непосредственной передаче объекта std::function конструктор копирования std::function предпочтительнее при разрешении перегрузки, поэтому он выбирается вместо шаблона конструктора.


Ответ на будущее: Если список захвата гарантированно будет пустым, вы также можете использовать обычные указатели на функции. В C ++ 0x лямбда без захвата неявно преобразуется в указатель на функцию. Таким образом, вы можете использовать что-то вроде

void foo(void (*t)(int, int)) { t(1, 2); }

void foo(void (*t)(int)) { t(1); }

и вызов foo напрямую с лямбда без захвата (или указатель на функцию с соответствующим типом).

Обратите внимание, что это преобразование является очень недавним дополнением к черновому стандарту языка (оно было добавлено в феврале этого года), поэтому вряд ли оно пока получит широкую поддержку. Visual C ++ 2010 пока не поддерживает его; Я не знаю о последних g ++.

3 голосов
/ 29 июля 2011

Я недавно думал о подобной проблеме, и когда я искал какие-либо известные решения, я наткнулся на этот пост и отсутствие решений для решения

Альтернативное решение - абстрагироваться над функтором как шаблоном.аргумент и использовать decltype для разрешения его типа.Итак, приведенный выше пример будет выглядеть так:

#include <iostream>
#include <functional>
using namespace std;

template<class F>
auto foo(F t) -> decltype(t(1,2))
{
    t(1, 2);
}

template<class F>
auto foo(F t) -> decltype(t(2)) 
{
    t(2);
}

int main()
{
     foo([](int a, int b){ cout << "a: " << a << " b: " << b << endl;});
}

Это работает, как и ожидалось, с gcc 4.5.

...