Pull to refresh

Comments 18

Не очень понимаю в чем заключается четырнадцатость/семнадцатость кода.
constexpr decltype(auto)?

Всё так, constexpr, decltype(auto), ну и из стандартной библиотеки пара функций, которые можно заменить на свои аналоги. + Было интересно именно для 14ого, т.к generic лямбды появились и хотелось бы, чтобы с ними тоже всё работало. Но можно поковырять и сделать поддержку 11-ого, да, без некоторых фич типа constexpr'а.
Скажите, стоило применять в самом первом примере auto в качестве возвращаемого параметра для описания происходящего? Как удобство в использовании оно бесспорно нужно, но как пример, особенно для полного понимания тем кто с такими трюками сталкивается первый раз, мне кажется вредно.

К сожалению, стоило, в противном случае мне пришлось бы писать там тип лямбды, что невозможно без decltype и auto, либо выносить лямбду в отдельный явный функтор, что ещё больше запутало бы. Еще как вариант там можно было бы написать std::function<int(int)>, но это была бы совсем другая история :)

Хорошая штука, да. Вариадик бинд проще сделать правильно идеологически, потому что ты чётко задаёшь количество аргументов и их место при связывании. С библиотекой из статьи так красиво не получится :(
И без меня понятно, что применять такое в реальных проектах не стоит


А вот я бы не был столь категоричен. Тут, скорее, нужно не «реальные проекты», а «коммерческие проекты, разрабатываемые командой». Во всяких вспомогательных тулах, которые тоже вполне реальны, почему бы и не использовать?

Главный минус, как это обычно бывает для тяжёлых темплейтов, во времени компиляции, даже приложенный пример компилируется как-то сильно долго…
Очень простой вопрос. Куда это применить и какую задачу это решает или упрощает?

Очень простой ответ: каррирование в исконном виде служит скорее чисто математическим целям (читать в сторону комбинаторной логики), на практике же даёт, например, возможность частичного применения, как описано в этой статье. А вот частичное применение, в свою очередь открывает путь к построению более сложных абстракций и концепций из области функционального программирования, что выливается в банальное сокращение объёмов кода при их написании. Но и без более высоких абстракций может принести пользу, там где используется всеми известный std::bind, например, который тоже является средством частичного применения.

Понятно. Чисто абстрактный приём, для того что бы было. В реальных задачах не нужен.
Это решает простую задачу — помогает программисту оставаться инженером, и не потухнуть от работы. А также помогает поддерживать голову в тонусе — пока попишешь, например, SFINAE, сразу мозг активирируется.
Объясните пожалуйста зачем используется нотация .template? строка 193 в файле kari.hpp: «return std::move(self_copy).template recurry();»? Ведь тут не возникает неоднозначности.
Не очень уверен во внутренних подробностях, но компилятор без этого совершенно не понимал происходящего ¯\_(ツ)_/¯
Там угловые скобки идут после recurry. Компилятор интерпретирует это как оператор меньше. Ключевое слово template указывает компилятору, что M это аргумент шаблона, а не операнд сравнения.

Похожая ситуация возникает, если мы обращаемся к вложенному типу (T::type). Ключевое слово typename помогает компилятору понять, что это тип а не статическое поле.
на 17 можно как-то так
template <typename R, typename ... Types> 
constexpr std::integral_constant<unsigned, sizeof ...(Types)> argument_cout( R(*f)(Types ...))
{
   return std::integral_constant<unsigned, sizeof ...(Types)>{};
}

auto curry = [](auto func, auto ... args)
{
	constexpr auto size_carry = sizeof...(args);
	if constexpr (size_carry == decltype(argument_cout(func))::value)
		return func(args...);
    else
	return [=](auto ... xs)
	{
		if constexpr (sizeof...(xs) + size_carry == decltype(argument_cout(func))::value)
			return func(args..., xs...);
		else
			return curry(func, args..., xs...);
	};
};

int value(int i, int f, int g)
{
    return i+f+g;
}

int main() {

std::cout << curry(value,1,2,3) << std::endl;
auto v1 = curry(value);
auto v2 = v1(1);
auto v3 = v2(2);
auto v4 = v3(3);
std::cout << curry(value)(1,2)(3) + v4 << std::endl;
return 0;
}
Можно, но добавь перфект форвардинг (прямую передачу?), возможности оборачивать функциональные объекты и т.д и получится одна гигантская нечитаемая функция :) Ну и с количеством аргументов функции ты с другой стороны зашёл, чем в kari.hpp. и прозрачно применять вложенные каррированные функции не получится. В общем с нужной допилкой получится по строчкам тоже самое, что и в моём подходе.
Sign up to leave a comment.

Articles