【问题标题】:How can I curry variadic template template parameters?如何使用可变参数模板模板参数?
【发布时间】:2014-01-28 13:22:13
【问题描述】:

可变模板模板参数接受任何模板:

template<typename T>
struct Test1 {
    using type = int;
};

template<typename T, typename T1>
struct Test2 {
    using type = char*;
};

template<template<typename...S> class BeCurry>
struct Currying {
};

using curry  = Currying<Test1>;
using curry2 = Currying<Test2>;

我想要 Currying 模板模板类。
这意味着:如果参数接受一个模板参数为Test1curry::apply&lt;T&gt;::type get Test1&lt;T&gt;::type。如果参数接受两个模板参数为Test2curry2::apply&lt;T0&gt; 是“部分”模板,curry2::apply&lt;T0&gt;::apply&lt;T1&gt;::type get Test2&lt;T0,T1&gt;::type

这可以实现吗?因为我无法查询模板模板参数的内部参数数量:

template<template<typename... S> class BeCurry>
struct Currying {
    enum { value = sizeof...(S) }; // error!
};

【问题讨论】:

  • 请查看我的更新答案

标签: c++ templates c++11 variadic-templates


【解决方案1】:

简单的解决方案是:

template
    <
        template <typename...> class BeCurry,
        typename... Params
    >
struct Currying
{
    template <typename... OtherParams>
    using curried = BeCurry<Params..., OtherParams...>;

    template <typename... OtherParams>
    using type = typename curried<OtherParams...>::type;

    template <typename... NewParams>
    using apply = Currying<curried, NewParams...>;
};

但由于编译错误(至少在 gcc 下),它不适用于 Test1Test2 等模板。此问题的解决方法如下所示:

template
    <
        template <typename...> class BeCurry,
        typename... Params
    >
struct Curry
{
    using type = BeCurry<Params...>;
};

template
    <
        template <typename...> class BeCurry
    >
struct Curry<BeCurry>
{
    using type = BeCurry<>;
};

现在行

template <typename... OtherParams>
using curried = BeCurry<Params..., OtherParams...>;

应该换成线条

template <typename... OtherParams>
using curried = typename Curry<BeCurry, Params..., OtherParams...>::type;

使用示例:

#include <iostream>
#include <typeinfo>

template <typename T>
void print_type(T t)
{
    std::cout << typeid(t).name() << std::endl;
}

// ...

print_type(Currying<Test1>::type<int>{});
print_type(Currying<Test1>::apply<int>::type<>{});
print_type(Currying<Test2>::type<int, char>{});
print_type(Currying<Test2>::apply<int>::type<char>{});
print_type(Currying<Test2>::apply<int>::apply<char>::type<>{});
print_type(Currying<Test2>::apply<int, char>::type<>{});

ideone 上的完整示例。

【讨论】:

    【解决方案2】:

    some problems of my own 之后,我提出了这个解决方案,它适用于任何模板类(也适用于您在帖子中提供的模板类)。
    这个解决方案的核心是is_valid_specialization,它作为柯里化过程是否可以认为完成的条件:

    #include <iostream>
    #include <type_traits>
    
    template<template<typename...> class C, typename... T>
    struct is_valid_specialization {
        typedef struct { char _; } yes;
        typedef struct { yes _[2]; } no;
    
        template<template<typename...> class D>
        static yes test(D<T...>*);
        template<template<typename...> class D>
        static no test(...);
    
        constexpr static bool value = (sizeof(test<C>(0)) == sizeof(yes));
    };
    
    namespace detail {
    
        template<template<typename...> class BeCurry, bool = false, typename... S>
        struct Currying {
    
            template<typename... T>
            using apply = Currying<BeCurry, is_valid_specialization<BeCurry, S..., T...>::value, S..., T...>;
        };
    
        template<template<typename...> class BeCurry, typename... S>
        struct Currying<BeCurry, true, S...> {
    
            template<typename... T>
            using apply = Currying<BeCurry, is_valid_specialization<BeCurry, S..., T...>::value, S..., T...>;
    
            using type = typename BeCurry<S...>::type;
        };
    }
    
    template<template<typename...> class BeCurry>
    using Currying = detail::Currying<BeCurry, is_valid_specialization<BeCurry>::value>;
    
    template<typename T>
    struct Test1 { using type = int; };
    
    template<typename T1, typename T2>
    struct Test2 { using type = char*; };
    
    template<typename...>
    struct Test3 { using type = double; };
    
    using curry  = Currying<Test1>;
    using curry2 = Currying<Test2>;
    using curry3 = Currying<Test3>;
    
    template<typename T>
    void pretty_print(T) {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    
    int main() {
        pretty_print(typename curry::apply<char>::type{});
        pretty_print(typename curry2::apply<int>::apply<char>::type{});
        pretty_print(typename curry3::type{});
    }
    

    ideone 上的输出

    【讨论】:

    • 是的,是最近的。但是 Currying 模拟 haskell 函数。所有应用模板只接受一个参数(生成结果或获取“部分”应用)。如果 apply 没有内部类型,它必须有一个应用模板....通过成功吃掉所有需要的参数。也许我可以根据你的结果成功......
    猜你喜欢
    • 2016-12-01
    • 2021-10-01
    • 2016-10-06
    • 1970-01-01
    • 1970-01-01
    • 2012-03-28
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    相关资源
    最近更新 更多