Я играю с С++ 11 lambdas и пытаюсь имитировать некоторую функцию из functional
module языка программирования D. Я действительно пытался реализовать curry
и compose
. Вот main
, который я пытаюсь получить:
int main()
{
auto add = [](int a, int b)
{
return a + b;
};
auto add5 = curry(add, 5);
auto composed = compose(add5, add);
// Expected result: 25
std::cout << composed(5, 15) << std::endl;
}
Проблема в том, что я не получаю тот же результат от g++ и clang++. Я получаю:
- 35 с g++ 4.8.1
- 25 с g++ 4.8.2
- 25 с g++ 4.9
- 32787 с clang++ 3.5 (соединительная линия, используемая с Coliru)
g++ 4.8.2 и 4.9 дают ожидаемый результат. Результаты, полученные из g++ 4.8.1 и clang 3.5, не зависят от значения, переданного в curry
. Сначала я подумал, что это может быть ошибка компилятора, но более вероятно, что у меня есть ошибка в моем коде.
Вот моя реализация curry
:
template<typename Function, typename First, std::size_t... Ind>
auto curry_impl(const Function& func, First&& first, indices<Ind...>)
-> std::function<
typename function_traits<Function>::result_type(
typename function_traits<Function>::template argument_type<Ind>...)>
{
return [&](typename function_traits<Function>::template argument_type<Ind>&&... args)
{
return func(
std::forward<First>(first),
std::forward<typename function_traits<Function>::template argument_type<Ind>>(args)...
);
};
}
template<typename Function, typename First,
typename Indices=indices_range<1, function_traits<Function>::arity>>
auto curry(Function&& func, First first)
-> decltype(curry_impl(std::forward<Function>(func), std::forward<First>(first), Indices()))
{
using FirstArg = typename function_traits<Function>::template argument_type<0>;
static_assert(std::is_convertible<First, FirstArg>::value,
"the value to be tied should be convertible to the type of the function first parameter");
return curry_impl(std::forward<Function>(func), std::forward<First>(first), Indices());
}
И вот моя реализация compose
(обратите внимание, что я написал только двоичный compose
, а D - переменный):
template<typename First, typename Second, std::size_t... Ind>
auto compose_impl(const First& first, const Second& second, indices<Ind...>)
-> std::function<
typename function_traits<First>::result_type(
typename function_traits<Second>::template argument_type<Ind>...)>
{
return [&](typename function_traits<Second>::template argument_type<Ind>&&... args)
{
return first(second(
std::forward<typename function_traits<Second>::template argument_type<Ind>>(args)...
));
};
}
template<typename First, typename Second,
typename Indices=make_indices<function_traits<Second>::arity>>
auto compose(First&& first, Second&& second)
-> decltype(compose_impl(std::forward<First>(first), std::forward<Second>(second), Indices()))
{
static_assert(function_traits<First>::arity == 1u,
"all the functions passed to compose, except the last one, must take exactly one parameter");
using Ret = typename function_traits<Second>::result_type;
using FirstArg = typename function_traits<First>::template argument_type<0>;
static_assert(std::is_convertible<Ret, FirstArg>::value,
"incompatible return types in compose");
return compose_impl(std::forward<First>(first), std::forward<Second>(second), Indices());
}
Класс function_trait
используется для получения arty, возвращаемого типа и типа аргументов лямбда. Этот код сильно зависит от трюка индексов. Поскольку я не использую С++ 14, я не использую std::index_sequence
, а более старую реализацию под названием indices
. indices_range<begin, end>
- последовательность индексов, соответствующая диапазону [begin, end)
. Вы можете найти реализацию этих вспомогательных метафайлов (а также curry
и compose
) в онлайн-версии кода, но они менее значимы в этой проблеме.
У меня есть ошибка в реализации curry
и/или compose
или плохие результаты (с g++ 4.8.1 и clang++ 3.5) из-за ошибок компилятора?
РЕДАКТИРОВАТЬ: Вы можете найти код, который не совсем читается. Итак, вот версии curry
и compose
, которые являются точно такими же, но используют шаблоны псевдонимов, чтобы уменьшить шаблон. Я также удалил static_assert
s; в то время как они могут быть полезной информацией, что слишком много текста для вопроса, и они не участвуют в этой проблеме.
template<typename Function, typename First, std::size_t... Ind>
auto curry_impl(const Function& func, First&& first, indices<Ind...>)
-> std::function<
result_type<Function>(
argument_type<Function, Ind>...)>
{
return [&](argument_type<Function, Ind>&&... args)
{
return func(
std::forward<First>(first),
std::forward<argument_type<Function, Ind>>(args)...
);
};
}
template<typename Function, typename First,
typename Indices=indices_range<1, function_traits<Function>::arity>>
auto curry(Function&& func, First first)
-> decltype(curry_impl(std::forward<Function>(func), std::forward<First>(first), Indices()))
{
return curry_impl(std::forward<Function>(func), std::forward<First>(first), Indices());
}
template<typename First, typename Second, std::size_t... Ind>
auto compose_impl(const First& first, const Second& second, indices<Ind...>)
-> std::function<
typename result_type<First>(
typename argument_type<Second, Ind>...)>
{
return [&](argument_type<Second, Ind>&&... args)
{
return first(second(
std::forward<argument_type<Second, Ind>>(args)...
));
};
}
template<typename First, typename Second,
typename Indices=make_indices<function_traits<Second>::arity>>
auto compose(First&& first, Second&& second)
-> decltype(compose_impl(std::forward<First>(first), std::forward<Second>(second), Indices()))
{
return compose_impl(std::forward<First>(first), std::forward<Second>(second), Indices());
}