Skip to main content
#include <type_traits>
#include <utility>

namespace utilities {
    template <typename Pack1, typename Pack2> struct merge;

    template <template <typename...> class P, typename... Ts, typename... Us>
    struct merge<P<Ts...>, P<Us...>> {
        using type = P<Ts..., Us...>;   
    };
}

template <std::size_t R, typename Pack, typename TypesIgnored, typename Output, typename = void> struct nPr_h;

template <template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<0, P<First, Rest...>, P<TypesIgnored...>, P<Output...>> {
    using type = P<P<Output...>>;  // Just one single pack (which must be wrapped in P so that the resulting merge
    // will gavegive all such single packs, rather than a merge of all the types).  
    using type = P<P<Output...>>;
};

template <std::size_t R, template <typename...> class P, typename TypesIgnored, typename Output>
struct nPr_h<R, P<>, TypesIgnored, Output> {
    using type = P<>;  // No pack can come out of this (permuting R types from nothing). 
    using type = P<>;
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
    using type = P<>;  // No pack can come out of this (since there are lessfewer types in
    // P<TypesIgnored..., Rest...> than R).
    using type = P<>;
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
    typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type,  // Case 1: 'First' is in the permuted pack (note that Output..., First are the
    // types in just one pack).  Now continue to get R-1 more types from
    // TypesIgnored..., Rest... (which are the remaining available types since
    // 'First' is no longer available for the remaining R-1 types, and the ignored
    // types are now P<> since we are starting a new nPr_h call).
    typename nPr_h<R-1, P<RestP<TypesIgnored...>, P<TypesIgnoredRest...>, First>P<>, P<Output...>>, First>>::type,
    // Case 2:  'First'First' in not in the permuted pack, so try to get R types from
    // Rest... to append to Output...  First is appended oto TypesIgnored... since
    // it is now among those types not used.
    typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type
> { };

template <std::size_t R, typename Pack> struct nPr;

template <std::size_t R, template <typename...> class P, typename... Ts>
struct nPr<R, P<Ts...>> : nPr_h<R, P<Ts...>, P<>, P<>> { };

// Testing
template <typename...> struct P;

int main() {
    static_assert(std::is_same<
        nPr<2, P<int, char, float>>::type,  
        P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
    >::value);

    static_assert(std::is_same<
        nPr<3, P<int, char, float, bool>>::type,
        P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
    >::value);
}
#include <type_traits>
#include <utility>

namespace utilities {
    template <typename Pack1, typename Pack2> struct merge;

    template <template <typename...> class P, typename... Ts, typename... Us>
    struct merge<P<Ts...>, P<Us...>> {
        using type = P<Ts..., Us...>;   
    };
}

template <std::size_t R, typename Pack, typename TypesIgnored, typename Output, typename = void> struct nPr_h;

template <template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<0, P<First, Rest...>, P<TypesIgnored...>, P<Output...>> {
    using type = P<P<Output...>>;  // Just one single pack (which must be wrapped in P so that the resulting merge will gave all such single packs, rather than a merge of all the types).  
};

template <std::size_t R, template <typename...> class P, typename TypesIgnored, typename Output>
struct nPr_h<R, P<>, TypesIgnored, Output> {
    using type = P<>;  // No pack can come out of this (permuting R types from nothing). 
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
    using type = P<>;  // No pack can come out of this (since there are less types in P<TypesIgnored..., Rest...> than R).
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
    typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type,  // Case 1: 'First' is in the permuted pack (note that Output..., First are the types in just one pack).  Now continue to get R-1 more types from TypesIgnored..., Rest... (which are the remaining available types since 'First' is no longer available for the remaining R-1 types, and the ignored types are now P<> since we are starting a new nPr_h call).
    typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type  // Case 2:  'First in not in the permuted pack, so try to get R types from Rest... to append to Output...  First is appended o TypesIgnored... since it is now among those types not used.
> { };

template <std::size_t R, typename Pack> struct nPr;

template <std::size_t R, template <typename...> class P, typename... Ts>
struct nPr<R, P<Ts...>> : nPr_h<R, P<Ts...>, P<>, P<>> { };

// Testing
template <typename...> struct P;

int main() {
    static_assert(std::is_same<
        nPr<2, P<int, char, float>>::type,  
        P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
    >::value);

    static_assert(std::is_same<
        nPr<3, P<int, char, float, bool>>::type,
        P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
    >::value);
}
#include <type_traits>
#include <utility>

namespace utilities {
    template <typename Pack1, typename Pack2> struct merge;

    template <template <typename...> class P, typename... Ts, typename... Us>
    struct merge<P<Ts...>, P<Us...>> {
        using type = P<Ts..., Us...>;   
    };
}

template <std::size_t R, typename Pack, typename TypesIgnored, typename Output, typename = void> struct nPr_h;

template <template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<0, P<First, Rest...>, P<TypesIgnored...>, P<Output...>> {
    // Just one single pack (which must be wrapped in P so that the resulting merge
    // will give all such single packs, rather than a merge of all the types).  
    using type = P<P<Output...>>;
};

template <std::size_t R, template <typename...> class P, typename TypesIgnored, typename Output>
struct nPr_h<R, P<>, TypesIgnored, Output> {
    // No pack can come out of this (permuting R types from nothing). 
    using type = P<>;
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
    // No pack can come out of this (since there are fewer types in
    // P<TypesIgnored..., Rest...> than R).
    using type = P<>;
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
    // Case 1: 'First' is in the permuted pack (note that Output..., First are the
    // types in just one pack).  Now continue to get R-1 more types from
    // TypesIgnored..., Rest... (which are the remaining available types since
    // 'First' is no longer available for the remaining R-1 types, and the ignored
    // types are now P<> since we are starting a new nPr_h call).
    typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type,
    // Case 2: 'First' in not in the permuted pack, so try to get R types from
    // Rest... to append to Output...  First is appended to TypesIgnored... since
    // it is now among those types not used.
    typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type
> { };

template <std::size_t R, typename Pack> struct nPr;

template <std::size_t R, template <typename...> class P, typename... Ts>
struct nPr<R, P<Ts...>> : nPr_h<R, P<Ts...>, P<>, P<>> { };

// Testing
template <typename...> struct P;

int main() {
    static_assert(std::is_same<
        nPr<2, P<int, char, float>>::type,  
        P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
    >::value);

    static_assert(std::is_same<
        nPr<3, P<int, char, float, bool>>::type,
        P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
    >::value);
}
deleted 31 characters in body
Source Link
prestokeys
  • 1.4k
  • 11
  • 19

I think I've found a better solution. It also generalizes to Permutation(N,R), with Permutation(N,N) being a special case that answers the original problem. This is shorter in code, and also more efficient because the O(N) RemoveFirstTypeFound metafunction is not being used at all.

#include <iostream>
#include <type_traits>
 
template <typename, typename> struct#include Merge;<utility>

template <template <typename...> class P, typename... Ts,namespace typename...utilities Us>{
struct Merge<P<Ts...>, P<Us...>> {
  template <typename usingPack1, typetypename =Pack2> P<Ts...,struct Us...>;
};merge;

    template <std::size_t<template N,<typename...> typenameclass PackP, typename... PreviousTs, typename... Output>Us>
    struct PermutationNHelper;merge<P<Ts...>, P<Us...>> {
        using type = P<Ts..., Us...>;   
    };
}

template <std::size_t N, template <typename...> class P, typename FirstR, typename... Rest, typename... PrevPack, typename... Output>
struct PermutationNHelper<N, P<First, Rest...>, P<Prev...>TypesIgnored, Output...> : Merge<
    typename PermutationNHelper<N-1, P<Prev..., Rest...>, P<>, Output..., First>::type,  // P<Prev..., Rest...> are the remaining elements, thus ensuring that the next element chosen will not be First.  The new Prev... is empty since we now start at the first element of P<Prev..., Rest...>.
    typename PermutationNHelper<N, P<Rest...>, P<Prev..., First>, Output...>::type  // Using P<Rest...> ensures that the next set of permutations will begin with the type after First, and thus the new Prev... is= Prev...,void> First.
>struct {};nPr_h;

template <std::size_t N, template<template <typename...> class P, typename PreviousFirst, typename... Rest, typename... TypesIgnored, typename... Output>
struct PermutationNHelper<NnPr_h<0, P<>P<First, PreviousRest...>, OutputP<TypesIgnored...>, P<Output...>> {
    using type = P<>;P<P<Output...>>;  // Just one single pack (which must be wrapped in P so that the resulting merge will gave all such single packs, rather than a merge of all the types).  
};

template <template<std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... PrevTypesIgnored, typename... Output>
struct PermutationNHelper<0, P<FirstnPr_h<R, Rest...>P<>, P<Prev...>TypesIgnored, Output...>Output> {  // P<Prev...> must be used (instead of simply 'Previous'), else there will be ambiguity.
    using type = P<P<Output...>>;P<>;  // Note that itNo mustpack becan P<P<Output...>>come insteadout of P<Output...> for thethis merging(permuting toR worktypes asfrom intendednothing). 
};

template <template<std::size_t R, template <typename...> class P, typename PreviousFirst, typename... Rest, typename... TypesIgnored, typename... Output>
struct PermutationNHelper<0nPr_h<R, P<>P<First, PreviousRest...>, OutputP<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
    using type = P<P<OutputP<>;  // No pack can come out of this (since there are less types in P<TypesIgnored...>>;, Rest...> than R).
};

template <std::size_t R, template <typename...> Pack>class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct EmptyPack;nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
    typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type,  // Case 1: 'First' is in the permuted pack (note that Output..., First are the types in just one pack).  Now continue to get R-1 more types from TypesIgnored..., Rest... (which are the remaining available types since 'First' is no longer available for the remaining R-1 types, and the ignored types are now P<> since we are starting a new nPr_h call).
    typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type  // Case 2:  'First in not in the permuted pack, so try to get R types from Rest... to append to Output...  First is appended o TypesIgnored... since it is now among those types not used.
> { };

template <template <typename...> class<std::size_t PR, typename... Ts>
struct EmptyPack<P<Ts...>> { using type =Pack> P<>;struct };nPr;

template <std::size_t NR, template <typename...> class P, typename... Pack>Ts>
usingstruct PermutationNnPr<R, =P<Ts...>> typename: PermutationNHelper<NnPr_h<R, PackP<Ts...>, typenameP<>, EmptyPack<Pack>::type>::type;P<>> { };

// Testing
template <typename PackOfPacks>...> struct PackSize;P;

int main() {
    static_assert(std::is_same<
        nPr<2, P<int, char, float>>::type,  
        P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
    >::value);

    static_assert(std::is_same<
        nPr<3, P<int, char, float, bool>>::type,
        P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
    >::value);
}

Incidentally, here is a longer solution, but one which has the advantage of having the implementation for nCr and all_permutations, which are useful in their own right:

#include <type_traits>
#include <utility>

namespace utilities {
    template <typename... Packs> struct merge;
    
    template <typename Pack>
    struct merge<Pack> {
        using type = Pack;
    };
    
    template <template <typename...> class P, typename... Ts, typename... Us>
    struct merge<P<Ts...>, P<Us...>> {
        using type = P<Ts..., Us...>;   
    };
    
    template <typename First, typename... Rest>
    struct merge<First, Rest...> : merge<First, typename merge<Rest...>::type> { };
    
    template <typename T, typename Pack> struct prepend;
    
    template <template <typename...> class P, typename... Ts, typename T>
    struct prepend<T, P<Ts...>> {
        using type = P<T, Ts...>;
    };
    
    template <typename T, typename PackOfPacks> struct prepend_to_each;
    
    template <typename T, template <typename...> class P, typename... Packs>
    struct prepend_to_each<T, P<Packs...>> {
        using type = P<typename prepend<T, Packs>::type...>;
    };
    
    template <typename Pack> struct pack_size;
    
    template <template <typename...> class P, typename... Ts>
    struct pack_size<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
    
//  all_rotations
    template <std::size_t, typename> struct rotate;

    template <template <typename...> class P, typename First, typename... Rest>
    struct rotate<0, P<First, Rest...>> {
        using type = P<First, Rest...>;
    };
    
    template <std::size_t N, template <typename...> class P, typename First, typename... Rest>
    struct rotate<N, P<First, Rest...>> : rotate<N - 1, P<Rest..., First>> { };
    
    template <typename Pack, typename Sequence> struct all_rotations_h;
    
    template <template <typename...> class P, typename... Ts, std::size_t... Is>
    struct all_rotations_h<P<Ts...>, std::index_sequence<Is...>> {
        using type = P<typename rotate<Is, P<Ts...>>::type...>;
    };
    
    template <typename Pack>
    struct all_rotations : all_rotations_h<Pack, std::make_index_sequence<pack_size<Pack>::value>> { };
}

// nCr
template <std::size_t R, typename Pack, typename = void> struct nCr;

template <template <typename...> class P, typename... Ts>
struct PackSize<P<TsnCr<1, P<Ts...>> :{
 std::integral_constant<std::size_t, sizeof  using type = P<P<Ts>...(Ts)>>; {  
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest>
struct nCr<R, P<First, Rest...>, std::enable_if_t<(R <= sizeof...(Rest) + 1 && R > 1)>> : utilities::merge<
    typename utilities::prepend_to_each<First, typename nCr<R-1, P<Rest...>>::type>::type,
    typename nCr<R, P<Rest...>>::type
> { };

template <std::size_t R, template <typename...> class P, typename First, typename... Rest>
struct nCr<R, P<First, Rest...>, std::enable_if_t<(R > sizeof...(Rest) + 1)>> {
    using type = P<>;
};

// all_permutations
template <typename Pack> struct all_permutations_h;

template <typename PackOfPacks> struct all_permutations_h_on_each;

template <template <typename...> class P, typename... Packs>
struct all_permutations_h_on_each<P<Packs...>> : utilities::merge<typename all_permutations_h<Packs>::type...> { };

template <typename Pack> struct
all_permutations : all_permutations_h_on_each<typename utilities::all_rotations<Pack>::type> { };

template <template <typename...> class P, typename Last>
struct all_permutations_h<P<Last>> {
    using Permutationtype = PermutationN<PackSize<Pack>P<P<Last>>;
};

template <template <typename...> class P, typename First, typename... Rest>
struct all_permutations_h<P<First, Rest...>> : utilities::valueprepend_to_each<First, Pack>;typename all_permutations<P<Rest...>>::type> { };

// We now combine nCr with all_permutations to get nPr.
template <typename PackOfPacks> struct all_permutations_on_each_pack;

template <template <typename...> class P, typename... Packs>
struct all_permutations_on_each_pack<P<Packs...>> : utilities::merge<typename all_permutations<Packs>::type...> { };

template <std::size_t R, typename Pack>
struct nPr : all_permutations_on_each_pack<typename nCr<R, Pack>::type> { };
    
    
// Testing
template <typename...> struct P;

int main() {
    std::cout << std::boolalpha << static_assert(std::is_same<
        PermutationN<1nPr<2, P<int, char, bool>>,
        P< P<int>, P<char>, P<bool> >
    >::value << '\n';  // true
    
    std::cout << stdfloat>>::is_same<
        PermutationN<2, P<inttype, char, bool>>,
        P< P<intP<P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
    >::value << '\n';  // true

    std::cout << std::is_same<
        PermutationN<3, P<int, char, bool>>,
        P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<charfloat>, boolP<float, int>, P<bool, int, char>P<char, P<boolfloat>, charP<float, int> >char>>
    >::value << '\n';  // true);

    static_assert(std::coutis_same<
 << std      nPr<3, P<int, char, float, bool>>::is_same<type,
        Permutation<P<intP<P<int, char, bool>>float>,
  P<int, float, char>, P<char, float, int>, P<char, P<int, float>, P<float, int, char>, P<float, char, int>, P<int, char, bool>, P<int, bool, char>, P<char, bool, int>, P<char, int, bool>, P<charP<bool, int, char>, P<bool, char, int>, P<int, float, bool>, P<int, bool, float>, P<float, bool, int>, P<float, int, bool>, P<bool, int, float>, P<bool, float, int>, P<char, float, bool>, P<char, bool, float>, P<float, bool, char>, P<float, char, bool>, P<bool, char, int>float>, >P<bool, float, char>>
    >::value << '\n';  // true);
}

I think I've found a better solution. It also generalizes to Permutation(N,R), with Permutation(N,N) being a special case that answers the original problem. This is shorter in code, and also more efficient because the O(N) RemoveFirstTypeFound metafunction is not being used at all.

#include <iostream>
#include <type_traits>
 
template <typename, typename> struct Merge;

template <template <typename...> class P, typename... Ts, typename... Us>
struct Merge<P<Ts...>, P<Us...>> {
    using type = P<Ts..., Us...>;
};

template <std::size_t N, typename Pack, typename Previous, typename... Output> struct PermutationNHelper;

template <std::size_t N, template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<N, P<First, Rest...>, P<Prev...>, Output...> : Merge<
    typename PermutationNHelper<N-1, P<Prev..., Rest...>, P<>, Output..., First>::type,  // P<Prev..., Rest...> are the remaining elements, thus ensuring that the next element chosen will not be First.  The new Prev... is empty since we now start at the first element of P<Prev..., Rest...>.
    typename PermutationNHelper<N, P<Rest...>, P<Prev..., First>, Output...>::type  // Using P<Rest...> ensures that the next set of permutations will begin with the type after First, and thus the new Prev... is Prev..., First.
> {};

template <std::size_t N, template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<N, P<>, Previous, Output...> {
    using type = P<>;
};

template <template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<0, P<First, Rest...>, P<Prev...>, Output...> {  // P<Prev...> must be used (instead of simply 'Previous'), else there will be ambiguity.
    using type = P<P<Output...>>;  // Note that it must be P<P<Output...>> instead of P<Output...> for the merging to work as intended.
};

template <template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<0, P<>, Previous, Output...> {
    using type = P<P<Output...>>;
};

template <typename Pack> struct EmptyPack;

template <template <typename...> class P, typename... Ts>
struct EmptyPack<P<Ts...>> { using type = P<>; };

template <std::size_t N, typename Pack>
using PermutationN = typename PermutationNHelper<N, Pack, typename EmptyPack<Pack>::type>::type;

template <typename PackOfPacks> struct PackSize;

template <template <typename...> class P, typename... Ts>
struct PackSize<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> {};

template <typename Pack>
using Permutation = PermutationN<PackSize<Pack>::value, Pack>;

// Testing
template <typename...> struct P;

int main() {
    std::cout << std::boolalpha << std::is_same<
        PermutationN<1, P<int, char, bool>>,
        P< P<int>, P<char>, P<bool> >
    >::value << '\n';  // true
    
    std::cout << std::is_same<
        PermutationN<2, P<int, char, bool>>,
        P< P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
    >::value << '\n';  // true

    std::cout << std::is_same<
        PermutationN<3, P<int, char, bool>>,
        P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<char, bool, int>, P<bool, int, char>, P<bool, char, int> >
    >::value << '\n';  // true

    std::cout << std::is_same<
        Permutation<P<int, char, bool>>,
         P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<char, bool, int>, P<bool, int, char>, P<bool, char, int> >
    >::value << '\n';  // true
}

I think I've found a better solution. This is shorter in code, and also more efficient because the O(N) RemoveFirstTypeFound metafunction is not being used at all.

#include <type_traits>
#include <utility>

namespace utilities {
    template <typename Pack1, typename Pack2> struct merge;

    template <template <typename...> class P, typename... Ts, typename... Us>
    struct merge<P<Ts...>, P<Us...>> {
        using type = P<Ts..., Us...>;   
    };
}

template <std::size_t R, typename Pack, typename TypesIgnored, typename Output, typename = void> struct nPr_h;

template <template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<0, P<First, Rest...>, P<TypesIgnored...>, P<Output...>> {
    using type = P<P<Output...>>;  // Just one single pack (which must be wrapped in P so that the resulting merge will gave all such single packs, rather than a merge of all the types).  
};

template <std::size_t R, template <typename...> class P, typename TypesIgnored, typename Output>
struct nPr_h<R, P<>, TypesIgnored, Output> {
    using type = P<>;  // No pack can come out of this (permuting R types from nothing). 
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R > sizeof...(Rest) + sizeof...(TypesIgnored))>> {
    using type = P<>;  // No pack can come out of this (since there are less types in P<TypesIgnored..., Rest...> than R).
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest, typename... TypesIgnored, typename... Output>
struct nPr_h<R, P<First, Rest...>, P<TypesIgnored...>, P<Output...>, std::enable_if_t<(R <= sizeof...(Rest) + sizeof...(TypesIgnored) && R != 0)>> : utilities::merge<
    typename nPr_h<R-1, P<TypesIgnored..., Rest...>, P<>, P<Output..., First>>::type,  // Case 1: 'First' is in the permuted pack (note that Output..., First are the types in just one pack).  Now continue to get R-1 more types from TypesIgnored..., Rest... (which are the remaining available types since 'First' is no longer available for the remaining R-1 types, and the ignored types are now P<> since we are starting a new nPr_h call).
    typename nPr_h<R, P<Rest...>, P<TypesIgnored..., First>, P<Output...>>::type  // Case 2:  'First in not in the permuted pack, so try to get R types from Rest... to append to Output...  First is appended o TypesIgnored... since it is now among those types not used.
> { };

template <std::size_t R, typename Pack> struct nPr;

template <std::size_t R, template <typename...> class P, typename... Ts>
struct nPr<R, P<Ts...>> : nPr_h<R, P<Ts...>, P<>, P<>> { };

// Testing
template <typename...> struct P;

int main() {
    static_assert(std::is_same<
        nPr<2, P<int, char, float>>::type,  
        P<P<int, char>, P<int, float>, P<char, int>, P<char, float>, P<float, int>, P<float, char>>
    >::value);

    static_assert(std::is_same<
        nPr<3, P<int, char, float, bool>>::type,
        P<P<int, char, float>, P<int, char, bool>, P<int, float, char>, P<int, float, bool>, P<int, bool, char>, P<int, bool, float>, P<char, int, float>, P<char, int, bool>, P<char, float, int>, P<char, float, bool>, P<char, bool, int>, P<char, bool, float>, P<float, int, char>, P<float, int, bool>, P<float, char, int>, P<float, char, bool>, P<float, bool, int>, P<float, bool, char>, P<bool, int, char>, P<bool, int, float>, P<bool, char, int>, P<bool, char, float>, P<bool, float, int>, P<bool, float, char>>
    >::value);
}

Incidentally, here is a longer solution, but one which has the advantage of having the implementation for nCr and all_permutations, which are useful in their own right:

#include <type_traits>
#include <utility>

namespace utilities {
    template <typename... Packs> struct merge;
    
    template <typename Pack>
    struct merge<Pack> {
        using type = Pack;
    };
    
    template <template <typename...> class P, typename... Ts, typename... Us>
    struct merge<P<Ts...>, P<Us...>> {
        using type = P<Ts..., Us...>;   
    };
    
    template <typename First, typename... Rest>
    struct merge<First, Rest...> : merge<First, typename merge<Rest...>::type> { };
    
    template <typename T, typename Pack> struct prepend;
    
    template <template <typename...> class P, typename... Ts, typename T>
    struct prepend<T, P<Ts...>> {
        using type = P<T, Ts...>;
    };
    
    template <typename T, typename PackOfPacks> struct prepend_to_each;
    
    template <typename T, template <typename...> class P, typename... Packs>
    struct prepend_to_each<T, P<Packs...>> {
        using type = P<typename prepend<T, Packs>::type...>;
    };
    
    template <typename Pack> struct pack_size;
    
    template <template <typename...> class P, typename... Ts>
    struct pack_size<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
    
//  all_rotations
    template <std::size_t, typename> struct rotate;

    template <template <typename...> class P, typename First, typename... Rest>
    struct rotate<0, P<First, Rest...>> {
        using type = P<First, Rest...>;
    };
    
    template <std::size_t N, template <typename...> class P, typename First, typename... Rest>
    struct rotate<N, P<First, Rest...>> : rotate<N - 1, P<Rest..., First>> { };
    
    template <typename Pack, typename Sequence> struct all_rotations_h;
    
    template <template <typename...> class P, typename... Ts, std::size_t... Is>
    struct all_rotations_h<P<Ts...>, std::index_sequence<Is...>> {
        using type = P<typename rotate<Is, P<Ts...>>::type...>;
    };
    
    template <typename Pack>
    struct all_rotations : all_rotations_h<Pack, std::make_index_sequence<pack_size<Pack>::value>> { };
}

// nCr
template <std::size_t R, typename Pack, typename = void> struct nCr;

template <template <typename...> class P, typename... Ts>
struct nCr<1, P<Ts...>> {
    using type = P<P<Ts>...>;   
};

template <std::size_t R, template <typename...> class P, typename First, typename... Rest>
struct nCr<R, P<First, Rest...>, std::enable_if_t<(R <= sizeof...(Rest) + 1 && R > 1)>> : utilities::merge<
    typename utilities::prepend_to_each<First, typename nCr<R-1, P<Rest...>>::type>::type,
    typename nCr<R, P<Rest...>>::type
> { };

template <std::size_t R, template <typename...> class P, typename First, typename... Rest>
struct nCr<R, P<First, Rest...>, std::enable_if_t<(R > sizeof...(Rest) + 1)>> {
    using type = P<>;
};

// all_permutations
template <typename Pack> struct all_permutations_h;

template <typename PackOfPacks> struct all_permutations_h_on_each;

template <template <typename...> class P, typename... Packs>
struct all_permutations_h_on_each<P<Packs...>> : utilities::merge<typename all_permutations_h<Packs>::type...> { };

template <typename Pack> struct
all_permutations : all_permutations_h_on_each<typename utilities::all_rotations<Pack>::type> { };

template <template <typename...> class P, typename Last>
struct all_permutations_h<P<Last>> {
    using type = P<P<Last>>;
};

template <template <typename...> class P, typename First, typename... Rest>
struct all_permutations_h<P<First, Rest...>> : utilities::prepend_to_each<First, typename all_permutations<P<Rest...>>::type> { };

// We now combine nCr with all_permutations to get nPr.
template <typename PackOfPacks> struct all_permutations_on_each_pack;

template <template <typename...> class P, typename... Packs>
struct all_permutations_on_each_pack<P<Packs...>> : utilities::merge<typename all_permutations<Packs>::type...> { };

template <std::size_t R, typename Pack>
struct nPr : all_permutations_on_each_pack<typename nCr<R, Pack>::type> { };
    
    
// Testing
template <typename...> struct P;

int main() {
    static_assert(std::is_same<
        nPr<2, P<int, char, float>>::type,  
        P<P<int, char>, P<char, int>, P<int, float>, P<float, int>, P<char, float>, P<float, char>>
    >::value);

    static_assert(std::is_same<
        nPr<3, P<int, char, float, bool>>::type,
        P<P<int, char, float>, P<int, float, char>, P<char, float, int>, P<char, int, float>, P<float, int, char>, P<float, char, int>, P<int, char, bool>, P<int, bool, char>, P<char, bool, int>, P<char, int, bool>, P<bool, int, char>, P<bool, char, int>, P<int, float, bool>, P<int, bool, float>, P<float, bool, int>, P<float, int, bool>, P<bool, int, float>, P<bool, float, int>, P<char, float, bool>, P<char, bool, float>, P<float, bool, char>, P<float, char, bool>, P<bool, char, float>, P<bool, float, char>>
    >::value);
}
Source Link
prestokeys
  • 1.4k
  • 11
  • 19

I think I've found a better solution. It also generalizes to Permutation(N,R), with Permutation(N,N) being a special case that answers the original problem. This is shorter in code, and also more efficient because the O(N) RemoveFirstTypeFound metafunction is not being used at all.

#include <iostream>
#include <type_traits>

template <typename, typename> struct Merge;

template <template <typename...> class P, typename... Ts, typename... Us>
struct Merge<P<Ts...>, P<Us...>> {
    using type = P<Ts..., Us...>;
};

template <std::size_t N, typename Pack, typename Previous, typename... Output> struct PermutationNHelper;

template <std::size_t N, template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<N, P<First, Rest...>, P<Prev...>, Output...> : Merge<
    typename PermutationNHelper<N-1, P<Prev..., Rest...>, P<>, Output..., First>::type,  // P<Prev..., Rest...> are the remaining elements, thus ensuring that the next element chosen will not be First.  The new Prev... is empty since we now start at the first element of P<Prev..., Rest...>.
    typename PermutationNHelper<N, P<Rest...>, P<Prev..., First>, Output...>::type  // Using P<Rest...> ensures that the next set of permutations will begin with the type after First, and thus the new Prev... is Prev..., First.
> {};

template <std::size_t N, template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<N, P<>, Previous, Output...> {
    using type = P<>;
};

template <template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<0, P<First, Rest...>, P<Prev...>, Output...> {  // P<Prev...> must be used (instead of simply 'Previous'), else there will be ambiguity.
    using type = P<P<Output...>>;  // Note that it must be P<P<Output...>> instead of P<Output...> for the merging to work as intended.
};

template <template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<0, P<>, Previous, Output...> {
    using type = P<P<Output...>>;
};

template <typename Pack> struct EmptyPack;

template <template <typename...> class P, typename... Ts>
struct EmptyPack<P<Ts...>> { using type = P<>; };

template <std::size_t N, typename Pack>
using PermutationN = typename PermutationNHelper<N, Pack, typename EmptyPack<Pack>::type>::type;

template <typename PackOfPacks> struct PackSize;

template <template <typename...> class P, typename... Ts>
struct PackSize<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> {};

template <typename Pack>
using Permutation = PermutationN<PackSize<Pack>::value, Pack>;

// Testing
template <typename...> struct P;

int main() {
    std::cout << std::boolalpha << std::is_same<
        PermutationN<1, P<int, char, bool>>,
        P< P<int>, P<char>, P<bool> >
    >::value << '\n';  // true
    
    std::cout << std::is_same<
        PermutationN<2, P<int, char, bool>>,
        P< P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
    >::value << '\n';  // true

    std::cout << std::is_same<
        PermutationN<3, P<int, char, bool>>,
        P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<char, bool, int>, P<bool, int, char>, P<bool, char, int> >
    >::value << '\n';  // true

    std::cout << std::is_same<
        Permutation<P<int, char, bool>>,
        P< P<int, char, bool>, P<int, bool, char>, P<char, int, bool>, P<char, bool, int>, P<bool, int, char>, P<bool, char, int> >
    >::value << '\n';  // true
}