【问题标题】:C++ Variadic Function Templates of Known Type已知类型的 C++ 可变参数函数模板
【发布时间】:2013-07-21 10:51:08
【问题描述】:

我目前正在尝试了解我可以通过可变参数模板支持做的一些事情。假设我有这样的功能 -

template <typename ... Args>
void foo(Args ... a)
{
    int len = sizeof...(tail);
    int vals[] = {a...};
    /* Rest of function */
}

/* Elsewhere */
foo(1, 2, 3, 4);

此代码有效,因为我事先假设参数将是整数,但如果我提供其他内容显然会失败。如果我知道参数包将预先包含特定类型,是否有某种方法可以在没有模板的情况下完成并具有类似 -

void foo(int ... a)

我尝试过这样做,但编译器给出了一个错误,提示 foo 是一个 void 字段。我知道我也可以通过递归访问包中的参数,但我不确定这会解决我遇到的问题 - 即我希望能够采用可变数量的相同类型的参数。

【问题讨论】:

  • 您可以使用enable_if 确保所有类型都是 ints。
  • 如果你把它放入一堆整数,代码就可以工作。如果你给它别的东西,它就不起作用。看来任务完成了。问题出在哪里?
  • 我想问题是是否有可能在编译时确保它们是整数。
  • A static_assert 也可以在这里工作,而不是 enable_if。两者都使用来自&lt;type_traits&gt;is_same
  • @znby 如果foo 正文中的任何内容要求所有类型为ints 或可转换为ints - 例如int vals[] = {a...}; - 如果你编写了一个它们不是的调用,那么你在编译时知道这一点。

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


【解决方案1】:

这应该可行:

void foo(int);

template<typename ...Args>
void foo(int first, Args... more)
{
   foo(first);
   foo(more...);
}

【讨论】:

  • 这个答案更适合 OP 的要求。该解决方案的唯一缺点是递归部分。但现在我可以看到更好的解决方案,即使使用 C++17 和折叠表达式也不行。
  • @mic 什么是更好的解决方案?这种方案的缺点是不能带0个参数。
  • @candy-chiu 我的最后一条评论中有一个错字:“但现在我看不到 ...”(澄清一下)。除此之外,您还可以轻松添加另一个重载:foo() 以支持 0 个参数。
  • std::forward 是多余的,除非使用 Args&amp;&amp; 代替 Args
【解决方案2】:

如果你知道之前的类型,你可以使用 std:initializer_list 的函数重载:

#include <initializer_list>
#include <iostream>

void foo( std::initializer_list<int> l )
{
    for ( auto el : l )
        // do something
}

void foo( std::initializer_list<float> l )
{
}

void foo( std::initializer_list<std::string> l )
{
}

int main()
{
    foo( {1, 2, 3, 4 } );
    foo( {1.1f, 2.1f, 3.1f, 4.1f } );
    foo( { "foo", "bar", "foo", "foo" } );
    return 0;
}

如果您使用 Visual Studio 2012,您可能需要Visual C++ Compiler November 2012 CTP

编辑:如果您仍想使用可变参数模板,您可以这样做:

template <int ... Args>
void foo( )
{
    int len = sizeof...(Args);
    int vals[] = {Args...};
    // ...
}

// And

foo<1, 2, 3, 4>();

但您必须记住,它不适用于floatstd::string,例如:您将以'float': illegal type for non-type template parameter 结尾。 floatnon-type template parameter 不合法,这与精度有关,浮点数无法精确表示,您引用同一类型的可能性取决于数字的表示方式。

【讨论】:

  • 如果你需要在编译时知道参数的数量,这个解决方法是不够的,因为initializer_list 不提供constexpr size() 成员。 AFAIK,没有办法绕过带有is_same&lt;&gt;/is_convertible&lt;&gt; 检查的可变参数模板。
  • @user2523017 你是对的,使用这个解决方案,我们不能在编译时获得参数的数量。
【解决方案3】:

我目前正在尝试了解我可以通过可变参数模板支持做的一些事情。

假设您想尝试使用可变参数模板,但找不到任何解决您的问题的方法,那么我建议您查看以下代码:

#include <iostream>

template<int ...Values>
void foo2()
{
    int len = sizeof...(Values);
    int vals[] = {Values...};

    for (int i = 0; i < len; ++i)
    {
        std::cout << vals[i] << std::endl;
    }
}

int main()
{
    foo2<1, 2, 3, 4>();

    return 0;
}

foo2 和你的foo 的区别在于你在运行时将参数传递给foo,在编译时传递给foo2,所以对于你使用的每个参数集,编译器都会生成单独的@987654326 @函数体。

【讨论】:

    【解决方案4】:

    如果这是您的偏好,将拒绝隐式转换为 int 的最佳答案的变体:

    #include <type_traits>
    
    void foo(int);
    
    template<typename Arg1, typename ...Args>
    void foo(Arg1 first, Args... more)
    {
       static_assert(std::is_same_v<Arg1, int>, "foo called with non-int argument");
       foo(first);
       foo(more...);
    }
    

    【讨论】:

      【解决方案5】:

      在大多数情况下,使用具有相同类型参数包的可变参数模板是没有意义的,但在启用 c++11 的编译器中会:

      #include <iostream>
      #include <tuple>
      
      template <typename ... Args>
      void foo(Args ... args) {
          using type = typename std::tuple_element<0, std::tuple<Args...>>::type;
          const auto len = std::tuple_size<std::tuple<Args...>> {};
          type vals[] = {args...};
      
          for (size_t it = 0; it < len; ++it) {
              std::cout << vals[it] << std::endl;
          }
      }
      
      int32_t main(int argc, char** argv) {
          foo(1, 2);
          foo(1.1, 2.2);
          foo("1", "2");
          return EXIT_SUCCESS;
      }
      

      【讨论】:

        猜你喜欢
        • 2012-07-10
        • 2011-12-14
        • 1970-01-01
        • 2023-04-02
        • 1970-01-01
        • 1970-01-01
        • 2021-10-01
        相关资源
        最近更新 更多