Pull to refresh

Лямбды в C++. Как это работает

Reading time1 min
Views9.6K

Рассмотрим такой пример кода:

void f(int i)
{
    auto g = [i](auto j)
    {
        return i + j;
    };
    g = [i](auto j)
    {
        return i - j;
    };
    g(1);
}

При компиляции возникнет ошибка в строке g = [i](auto j).

Почему так происходит?

Дело в том, что лямбды в C++ — это всего-лишь синтаксический сахар над локальными классами/структурами с определённым [возможно шаблонным] оператором "скобки":
class Lambda1
{
    int i;

public:
    Lambda1(int i) : i(i) {}

    template <class Ty> auto operator()(Ty j)
    {
        return i + j;
    }
};

class Lambda2
{
    int i;

public:
    Lambda2(int i) : i(i) {}

    template <class Ty> auto operator()(Ty j)
    {
        return i - j;
    }
};

void f(int i)
{
    auto g = Lambda1(i);
    g = Lambda2(i);
    g(1);
}


Теперь причина ошибки [в строке g = Lambda2(i);] становится очевидной: Lambda1 и Lambda2 — это различные классы, которые ничего не знают друг о друге.

Данные классы пришлось сделать глобальными, т.к. C++ всё ещё не поддерживает шаблонные методы в локальных классах.

В будущих версиях C++ вполне валидно будет писать так:
void f(int i)
{
    class Lambda1
    {
        int i;

    public:
        Lambda1(int i) : i(i) {}

        template <class Ty> auto operator()(Ty j)
        {
            return i + j;
        }
    };
    auto g = Lambda1(i);
    class Lambda2
    {
        int i;

    public:
        Lambda2(int i) : i(i) {}

        template <class Ty> auto operator()(Ty j)
        {
            return i - j;
        }
    };
    g = Lambda2(i);
    g(1);
}



И в заключение.

Чтобы приведённые примеры кода скомпилировались достаточно заменить auto g на std::function<int(int)> g.
(Как работает std::function — это уже тема для отдельной статьи.)
Tags:
Hubs:
Total votes 18: ↑4 and ↓14-10
Comments9

Articles