【问题标题】:Generic template template parameters通用模板模板参数
【发布时间】:2017-10-18 14:43:53
【问题描述】:

厌倦了“统一初始化”不是很统一,我决定编写一个通用的 construct() 包装器,如果类型是聚合则使用聚合初始化,否则直接初始化:

template <class T, class... Args,
          std::enable_if_t<std::is_aggregate_v<T>, int> = 0>
constexpr auto construct(Args&&... args)
    -> decltype(T{std::forward<Args>(args)...})
{
    return T{std::forward<Args>(args)...};
}

template <class T, class... Args>
constexpr auto construct(Args&&... args)
   -> decltype(T(std::forward<Args>(args)...))
{
    return T(std::forward<Args>(args)...);
}

这很好用:

template <class T, class U>
struct my_pair { T first; U second; };

auto p = construct<my_pair<int, float>>(1, 3.14f);
auto v = construct<std::vector<int>>(5, 0);

我想扩展它以使用构造函数的模板参数推导。所以我添加了另一对重载:

template <template <class...> class T, // <-- (1) 
          class... Args,
          class A = decltype(T{std::declval<Args>()...}),
          std::enable_if_t<std::is_aggregate_v<A>, int> = 0>
constexpr auto construct(Args&&... args)
    -> decltype(T{std::forward<Args>(args)...})
{
     return T{std::forward<Args>(args)...};
}

template <template <class...> class T, // <-- (1)
          class... Args>
constexpr auto construct(Args&&... args)
    -> decltype(T(std::forward<Args>(args)...))
{
    return T(std::forward<Args>(args)...);
}

也许令人惊讶(至少对我而言),这适用于简单的情况:

// deduction guide for my_pair
template <class T, class U> my_pair(T&&, U&&) -> my_pair<T, U>;

auto p = construct<my_pair>(1, 3.14f); // my_pair<int, float>
auto v = construct<std::vector>(5, 0); // vector of 5 ints

不幸的是,在尝试调用时失败了

auto a = construct<std::array>(1, 2, 3); // No matching call to construct()

因为std::array有一个非类型模板参数,所以与(1)处的template &lt;class...&gt; class T模板模板参数不匹配。

所以我的问题是,有没有办法在 (1) 处制定参数,以便它可以接受 any 类模板名称,而不管其类型(类型或非类型)如何模板参数?

【问题讨论】:

    标签: c++ templates c++17 template-templates


    【解决方案1】:

    不幸的是,没有代码重复的正确方法。 C++17中新增的“auto作为模板参数”只支持非类型模板参数

    我认为这可行的唯一方法是使用代码生成器生成固定数量的autoclass 排列。例如

    template <
        template <class, auto...> class T,
        class... Args>
    constexpr auto construct(Args&&... args) // ...
    
    template <
        template <class, auto, class...> class T,
        class... Args>
    constexpr auto construct(Args&&... args) // ...
    
    template <
        template <auto, class, auto...> class T,
        class... Args>
    constexpr auto construct(Args&&... args) // ...
    
    template <
        template <auto, class, auto, class...> class T,
        class... Args>
    constexpr auto construct(Args&&... args) // ...
    
    template <
        template <auto, class, auto, class, auto...> class T,
        class... Args>
    constexpr auto construct(Args&&... args) // ...
    
    // and so on...
    

    live example on wandbox

    听起来你有一个好主意……

    【讨论】:

    • template &lt;class, auto...&gt; 似乎足够了? :) 可能涵盖了所有真实案例(std::arraystd::integer_sequence)。
    • @Barry:在 99% 的情况下它可能已经足够好了,但是如果我分发一个包含 construct 的库,我可能会生成更多的情况以确保它可以处理 99.9999 % :)
    • sigh 谢谢,这是我期待的答案,但我认为可能有一个奇特的解决方法。 “听起来你对提案有个好主意”——我在考虑这个a while ago,C++17 之前的版本。 std::is_aggregate 和构造函数类型推导改变了一些东西。
    猜你喜欢
    • 2018-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    相关资源
    最近更新 更多