【问题标题】:Variadic function that accepts arguments of same type at compile time and iterate on them在编译时接受相同类型的参数并对其进行迭代的可变参数函数
【发布时间】:2020-08-28 05:27:57
【问题描述】:

我正在寻找一种方法来实现可变参数函数,该函数在编译时接受相同类型的参数并且应该能够迭代他们。可变参数在最后,它们都具有相同的类型。 像下面这样 -

void SampleFunc(Other arguments(String may be)..., int... arg)
{
   for (const auto& val : arg)
   {
      // Each argument available here.
   }
}

然后我会像下面这样调用这个函数-

SampleFunc("String", "{1,2,3,4})

最重要的是每次调用函数时可变参数都是硬编码的,所以我应该能够在编译时生成这个可变参数。

现在我正在接受如下所示的函数参数-

void SampleFunc(std::string str, std::vector<int>& nums)

但这增加了每次调用函数时构建向量的运行时间成本,这是我想避免的。


更新:-

我忘了说这个函数一开始还有其他参数。这是我的错,对此感到抱歉。我现在更新了我的示例。

【问题讨论】:

  • 致您的更新:您仍然可以使用下面给出的答案,只需更改函数签名:template &lt;typename... Args&gt; void SampleFunc(const atd::string&amp; str, /* other arguments */ , Args&amp;&amp;... args)

标签: c++ templates c++17 variadic-functions function-templates


【解决方案1】:

如果参数在编译时已知,则在 中,使用 fold expressions 您可以执行类似的操作

#include <utility> // std::forward

template <typename Type>
void doSomething(Type&& arg) /* noexcept */
{
   // Each argument available here.
   std::cout << arg << "\n";
}

template <typename... Args> 
void SampleFunc(Args&&... args) /* noexcept */
{
   (doSomething(std::forward<Args>(args)), ...);
}

现在你可以像这样调用函数了

SampleFunc(1, 2, 3, 4);   

并且使用doSomething 你可以对每个参数做一些事情。

(See a Demo Online)


在以前的编译器中,你可以模仿the fold expression via expander trick,如下

template <typename Type>
void doSomething(Type&& arg) /* noexcept */
{
   // Each argument available here.
   std::cout << arg << "\n";
}

template <typename... Args> 
void SampleFunc(Args&&... args) /* noexcept */
{
   using dummy = int[];
   (void)dummy {
      0, (doSomething(std::forward<Args>(args)), 0)...
   };
}

(See a Demo Online)

【讨论】:

    【解决方案2】:

    迭代可变参数是最简单的部分:您标记了 C++17,以便可以使用 JeJo 建议的模板折叠或其他方式(递归、未使用数组的初始化)。

    更复杂的是,所有的参数都是完全相同的类型。

    显然你可以使用 SFINAE 来强制推导的类型是相同的类型,但是如果你传递不同类型的参数,例如

    foo(1l, 2l, 3l, 4); // long, long, long, int
    

    当一个参数可以转换为其他参数的类型时,代码不会编译。

    如果您接受传递附加函数并且您的函数是模板结构的方法,则可以从 using 开始,从一对类型/索引中选择类型

    template <typename T, std::size_t>
    using get_type = T;
    

    模板结构可以这样写

    template <typename...>
    struct bar;
    
    template <typename T, std::size_t ... Is>
    struct bar<T, std::index_sequence<Is...>>
     {
       void operator() (std::string const & str, get_type<T, Is> const & ... ts)
        { ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
     };
    

    注意operator()str 后面的参数都是T 类型,其中T 是结构的第一个模板参数。

    附加功能是

    template <typename T, typename ... Ts>
    void foo (std::string const & str, Ts const & ... ts)
     { bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
    

    您可以拨打foo()如下

    foo<int>("string", 1, 2, 3, 4l);
    

    观察到 long 值 (4l) 被接受,因为它被转换为 int

    如果您愿意,也可以直接拨打bar::operator()

    bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
    

    但你必须明确第二个模板参数,所以会有一些冗余。

    以下是完整的编译示例

    #include <string>
    #include <utility>
    #include <iostream>
    
    template <typename T, std::size_t>
    using get_type = T;
    
    template <typename...>
    struct bar;
    
    template <typename T, std::size_t ... Is>
    struct bar<T, std::index_sequence<Is...>>
     {
       void operator() (std::string const & str, get_type<T, Is> const & ... ts)
        { ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
     };
    
    template <typename T, typename ... Ts>
    void foo (std::string const & str, Ts const & ... ts)
     { bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
    
    int main ()
     {
       foo<int>("string", 1, 2, 3, 4l); // a long value is converted to int
    
       bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
     }
    

    【讨论】:

      【解决方案3】:

      可变参数在最后,它们都具有相同的类型。

      std::vector 可能有额外分配的开销,您可以简单地使用std::initializer_list 代替(可变参数)。

      void SampleFunc(std::string str, std::initializer_list<int>& nums)
      {
          for (int val : nums)
          {
              // Each argument available here.
          }
      }
      

      与调用类似

      SampleFunc("String", {1, 2, 3, 4});
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多