【问题标题】:Variadic function templates with two (or more) specific packs (specialization/overloading)具有两个(或更多)特定包(专业化/重载)的可变参数函数模板
【发布时间】:2020-04-06 10:47:23
【问题描述】:

函数“Process”正在接受可变数量的可变类型参数。为了处理不同的情况,我成功地重载了它:

// general case
template <typename ...Types>
void Process( const Types&... items )

// single T
template <typename T>
void Process( const T& t )

// one or more of type NVP<>
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps )

我想做但不能做的事情如下:对于具有任意数量的 ATT&lt;&gt; 类型的前导参数后跟任意数量的 @987654325 的情况,我需要重载@像这样:

// any number of leading Types ATT<> followed by any number of NVP<>
template <typename ...ATypes, typename ...BTypes>
void Process( const ATT<ATypes>&... atts, const NVP<BTypes>&... nvps )

起初你会认为编译器应该很容易匹配这个,如果它已经可以做其他情况的话。这里绝对没有歧义!?但是,匹配失败,没有错误消息,只是编译器忽略了所需的重载。

目前使用 VS2017 和 /std:c++17


注意事项:

1. 显然,可以为 one 前导类型 ATT&lt;T1&gt; 像这样

// one leading Type ATT<T1>
template <typename T1, typename ...Types>
void Process( const ATT<T1>& a1, const Types&... remaining )

但不止一个,我需要做一些丑陋的手动递归。 我真的很想拥有一整包领先的ATT&lt;...&gt;

2.我知道,一般类型的前导参数包在匹配时总是模棱两可,但对于像 ATT&lt;ATypes&gt;... 这样的专业化,则不应该存在模棱两可的情况。

【问题讨论】:

  • 或者,这是另一种可能的方法:stackoverflow.com/questions/48739516/…
  • 感谢您迄今为止的建议。 50个软糖点真的很有吸引力,嗯。是的,另一个级别的间接(元组、结构、模板等)会像往常一样解决它。但是,应该有一个更简单的方法。也许必须等待 C++33。
  • 至于为什么它不像你想象的那么容易。想象一个参数 X,它既不是ATT 也不是NVP。但是两者都可以从 X 构造。显然,更好的编译器只有在这种情况发生时才会抱怨。但我们在这里......
  • 我不知道也不确定隐式构造/转换是否应该发生在这样的模板化参数或一般模板中。

标签: c++ c++17 variadic-templates variadic-functions overload-resolution


【解决方案1】:

您可以根据Types... 是否与ATT&lt;T&gt;..., NVP&lt;U&gt;... 匹配,从const Types&amp;... 重载中分派。

这里的基本策略是找到最后一个ATT&lt;T&gt;的索引,将所有内容作为一个元组转发,然后使用适当的索引序列进行索引以转发到另一个函数,其中ATT值和NVP值在两个元组:

namespace detail {
    template<class...>
    struct get_split_index;

    template<class T, class... Others>
    struct get_split_index<T, Others...> {
        static constexpr std::size_t i = -1;
    };

    template<class T, class... Others>
    struct get_split_index<ATT<T>, Others...> {
        static constexpr std::size_t next = get_split_index<Others...>::i;
        static constexpr std::size_t i = next == -1 ? -1 : next + 1u;
    };

    template<class T, class... Others>
    struct get_split_index<NVP<T>, Others...> {
        // will be 0 if the rest are all NVP<T>, otherwise -1
        static constexpr std::size_t i = get_split_index<Others...>::i;
    };

    template<>
    struct get_split_index<> {
        static constexpr std::size_t i = 0;
    };

    template<typename... ATypes, typename... BTypes, std::size_t... ATT_I, std::size_t... NVP_I>
    void Process(const std::tuple<const ATT<ATypes>&...>& att, const std::tuple<const NVP<BTypes>&...>& nvp, std::index_sequence<ATT_I...>, std::index_sequence<NVP_I...>) {
        // Use (std::get<ATT_I>(att)) and (std::get<NVP_I>(nvp))
        // instead of (atts) and (nvps) that you would use in your
        // supposed `void Process(const ATT<ATypes>&..., const NVP<BTypes>&...)`
    }

    template<typename... Types, std::size_t... ATT_I, std::size_t... NVP_I>
    void ProcessDispatch(const std::tuple<Types...>& t, std::index_sequence<ATT_I...> att_i, std::index_sequence<NVP_I...> nvp_i) {
        detail::Process(std::forward_as_tuple(std::get<ATT_I>(t)...), std::forward_as_tuple(std::get<NVP_I + sizeof...(ATT_I)>(t)...), att_i, nvp_i);
    }
}

template <typename ...Types>
void Process( const Types&... items ) {
    constexpr std::size_t split_index = detail::get_split_index<Types...>::i;
    if constexpr (split_index != -1) {
        // Might want to check `&& sizeof...(Types) != 0`
        detail::ProcessDispatch(std::forward_as_tuple(items...), std::make_index_sequence<split_index>{}, std::make_index_sequence<sizeof...(Types) - split_index>{});
    } else {
        // general case
    }
}

template <typename T>
void Process( const T& t ) {
    // single T
}


template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps ) {
    // one or more of type NVP<>
    // This can also be folded into `detail::Process`, checking
    // `if constexpr (sizeof...(BTypes) == 0)`.
}

【讨论】:

    【解决方案2】:

    相信你可以使用结构来帮助你。编译器无法确定一个参数包在哪里停止而另一个在哪里开始,请考虑:

    foo(1, 2.0, '3', "45", 6.0f)。第一个参数包可以什么都不是,第一个,全部或以上都不是。没有特别的理由偏爱一个。所以你不能制作一个接受两个可变参数的函数。您可以做的是将其拆分为两个结构,并明确指定外部类的参数。

    template<typename... Args>
    struct S
    {
        template<typename... Inner>
        static void Process(const ATT<Args>&... atts, const NVP<Inner>&... nvps) {}
    };
    

    使用示例:

    ATT<double> a1;
    ATT<long> a2;
    NVP<int> n1;
    NVP<const char*> n2;
    
    S<double, long>::Process(a1, a2, n1, n2);
    

    另一个版本可能是使用构造函数。在这里,您还可以获得更容易的自动扣除。不幸的是,它只适用于 C++17 及更高版本。

    template<typename... Args>
    struct S
    {
        std::tuple<ATT<Args>...> tup;
    
        S(const ATT<Args>&... atts)
            : tup(atts...)
        {}
    
        template<typename... Inner>
        void Process(const NVP<Inner>&... nvps){}
    };
    
    template<typename... Args>
    S(const ATT<Args>&... atts)->S<Args...>;
    

    而用法是:

    S(ATT(1), ATT(3.4)).Process(NVP("asdf"), NVP(3.4), NVP('f'));
    return 0;
    

    【讨论】:

      【解决方案3】:

      假设您可以将它们作为元组获取,我是在从 https://stackoverflow.com/a/12782697/1480324 绘制后制作的:

      #include <iostream>
      #include <tuple>
      
      template<typename T>
      struct ATT {};
      
      template<typename T>
      struct NVP {};
      
      template<typename... ATTs, typename... NVPs>
      void Process(const std::tuple<ATT<ATTs>...>& atts, const std::tuple<NVP<NVPs>...>& nvps) {
          std::cout << sizeof...(ATTs) << std::endl;
          std::cout << sizeof...(NVPs) << std::endl;
      }
      
      int main() {
          Process(std::make_tuple(ATT<int>(), ATT<double>()), std::make_tuple(NVP<std::string>(), NVP<bool>()));
      
          return 0;
      }
      

      它在 https://www.onlinegdb.com/online_c++_compiler 上编译,但我无法在 Visual Studio 中进行测试。

      【讨论】:

        猜你喜欢
        • 2011-12-07
        • 2018-06-06
        • 1970-01-01
        • 1970-01-01
        • 2020-04-09
        • 2011-10-29
        • 1970-01-01
        • 2021-01-24
        相关资源
        最近更新 更多