Реализация (типизированного) комбинатора K в C++

Я пытаюсь реализовать комбинатор K из исчисления комбинатора SK на С++. Комбинатор K — это функция высшего порядка, которая обычно принимает некоторое значение x и возвращает что-то который в свою очередь принимает значение y и возвращает из него x. Другими словами,

K(x)(y) == x

или пошагово:

intermediate = K(x)
intermediate(y) == x

Способность обращаться с K(x) как с вещью в себе, независимой от y, необходима. Кроме того, нет необходимости указывать тип y при простом создании K(x) без вызова его на y. Тип y можно указать, когда K(x)(y) вычисляется где-то в коде.

Я пытаюсь исправить написанный мной код, который пытается реализовать комбинатор K:

#include <iostream>

template<class A>
template<class B>
auto K = [](A x) {
    return [=](B y) {
        return x;
    };
};

int main()
{
    std::cout << "Hello world!\n";
    auto Kx = K<int>(3);
    auto Kxy = Kx<float>(4.5);
    std::cout << Kxy << std::endl;
}

Выводит error: extraneous template parameter list in template specialization or out-of-line template definition. Я пытался настроить параметры шаблона и перемещать их, но безрезультатно. Кто-нибудь знает, как я могу исправить эту ошибку?


person user76284    schedule 15.02.2016    source источник


Ответы (3)


Лямбды не могут быть шаблонами. Вы можете сделать это, хотя:

#include <iostream>

auto K = [](auto x) {
    return [=](auto y) {
        return x;
    };
};

int main()
{
    std::cout << "Hello world!\n";
    auto Kx = K(3);
    auto Kxy = Kx(4.5);
    std::cout << Kxy << std::endl;
}

Они называются универсальными лямбда-выражениями (существуют с C++14), и в основном это то, что вам нужно. Их operator() является шаблоном для каждого параметра auto.

person yuri kilochek    schedule 15.02.2016
comment
Вы можете иметь шаблонные переменные в С++ 14. Вы также можете добавить примечание о том, что универсальные лямбда-выражения также являются частью C++14. - person Some programmer dude; 15.02.2016
comment
@JoachimPileborg, а это совсем другое. - person yuri kilochek; 15.02.2016

Это вполне возможно, даже если у вас нет C++14 — просто помните, что лямбды — это просто сокращенная запись для объектов функторов. Поэтому мы можем создать объект, представляющий промежуточное значение:

template<class A>
class Intermediate {
  A m_a;
public:
  explicit Intermediate(const A& a)
  : m_a(a)
  {}

  template<class B>
  A operator()(const B&) const
  { return m_a; }
};

template<class A>
Intermediate<A> K(const A& a)
{ return Intermediate<A>(a); }

Отдельная функция K необходима для вывода аргументов шаблона, чтобы мы могли написать K(x)(y).

person amon    schedule 15.02.2016

Вы можете «решить» это следующим изменением:

template<class A, class B>
auto K = [](A x) {
    return [=](B y) {
        return x;
    };
};

Однако затем вам также необходимо изменить вызовы:

auto Kx = K<int, float>(3);
auto Kxy = Kx(4.5);

В качестве поясняющей заметки: хотя сами лямбда-выражения не могут быть шаблонными, но переменные из С++ 14 могут, и я объявляю K как шаблонную переменную, так уж получилось, что переменная объявлена ​​как лямбда-объект .

person Some programmer dude    schedule 15.02.2016
comment
Такой синтаксис реален? ideone.com/gjWyf5 Вы должны посмотреть на ответ Юрия. - person xaxxon; 15.02.2016
comment
@xaxxon Ответ Юрия лучше, поэтому я проголосовал за него, но он хорошо работает в С++ 14 с использованием старого компилятора clang. Возможно, это ошибка в clang, или это может быть ошибка в GCC на ideone, я не знаю. - person Some programmer dude; 15.02.2016