std::string

int

std::map

std::map<std::string, int>

std::map

#include <type_traits> #include <functional> #include <map> #include <iostream> template <template <class...> class C, class... T, class D = C<T...>> constexpr std::true_type valid(std::nullptr_t); template <template <class...> class C, class... T> constexpr std::false_type valid(...); template <class TrueFalse, template <class...> class C, class... ArgsSoFar> struct curry_impl; template <template <class...> class C, class... ArgsSoFar> struct curry_impl<std::true_type, C, ArgsSoFar...> { using type = C<ArgsSoFar...>; }; template <template <class...> class C, class... ArgsSoFar> struct curry_impl<std::false_type, C, ArgsSoFar...> { template <class... MoreArgs> using apply = curry_impl<decltype(valid<C, ArgsSoFar..., MoreArgs...>(nullptr)), C, ArgsSoFar..., MoreArgs...>; }; template <template <class...> class C> struct curry { template <class... U> using apply = curry_impl<decltype(valid<C, U...>(nullptr)), C, U...>; }; int main(void) { using CurriedIsSame = curry<std::is_same>; static_assert(curry<std::is_same>::apply<int>::apply<int>::type::value); curry<std::less>::apply<int>::type less; std::cout << std::boolalpha << less(5, 4); // prints false using CurriedMap = curry<std::map>; using MapType = CurriedMap::apply<int>::apply<long, std::less<int>, std::allocator<std::pair<const int, long>>>::type; static_assert(std::is_same<MapType, std::map<int, long>>::value); }

valid

std::true_type

C<T..>

C

T...

std::false_type

C

curry_impl

std::true_type

valid

std::true_type

ArgsSoFar

curry_impl<C, ArgsSoFar...>::type

C<ArgsSoFar...>

std::map

std::map<int, long>

std::map<int, long, std::less<int>>

std::map<int, long, std::less<int>, std::pair<const int, long>>

ArgsSoFar

curry_impl<std::false_type>

ArgsSoFar

MoreArgs

apply

ArgsSoFar

MoreArgs

curry_impl<std::true_type>

Currying is the technique of transforming a function that takes multiple arguments in such a way that it can be called as a chain of functions, each with a single argument. I've discussed Currying on this blog previously in Fun With Lambdas C++14 Style and Dependently-Typed Curried printf . Both blogposts discuss currying of functions proper. I.e., they discuss how C++ can treat functions as values at runtime.However, currying is not limited to just functions. Types can also be curried---if they take type arguments. In C++, we call them templates. Templates are "functions" at type level. For example, passing two type argumentsandtogives. Sois a type-level function that takes two (type) arguments and gives another type as a result. They are also known as type constructors.So, the question today is: Can C++ templates be curried? As it turns out, they can be. Rather easily. So, here we go...The technique is very simple. There's a function calledthat has two overloads. The first one returnsonly ifis a valid instantiation of templatewith argument list. Otherwise, it returnsis type constructor that we would like to curry. This function uses the SFINAE idiom.is the core implementation of template currying. It has two specializations. Thespecialization is selected whenreturns. I.e., curried version of the type constructor has received the minimum number of type arguments to form a complete type. In other words,are enough.is same as instantiation of the type constructor with the valid type arguments ().Note that C++ allows templates to have default type arguments. Therefore, a template could be instantiated by providing "minimum" number of arguments. For example,could be instantiated in three ways giving the same type:Whenare not enough,carries the partial list of type arguments () at class template level. It allows passing one or more type arguments () to the type constructor through thetypedef. Whenandare enough to form a valid instantiation,is chosen which yields the fully instantiated type.