【问题标题】:accessing first n variadic function arguments访问前 n 个可变参数函数参数
【发布时间】:2023-03-30 15:13:01
【问题描述】:

我有以下代码:

template<size_t sz,typename T=float> class Vec{
    T v[sz];    
    Vec(const T& val,const T&... nv){
        //how do i assign `sz` number of first arguments into `this->v` array
    }
}

我想创建构造函数,它接收构造函数参数的通用编号,并将第一个 sz 参数数量分配给 v 的成员变量

我想做的,就是能够这样做:Vec&lt;3&gt; var(1.0,2.0,3.0);

【问题讨论】:

  • 那不是有效代码(模板声明无效,... 的使用也无效)。
  • val 是将它与另一个构造函数区分开来,并强制执行至少必须有一个参数

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


【解决方案1】:

首先声明这个实用函数:

template <typename T> inline void push(T* p) {}
template <typename T, typename First, typename... Args>
inline void push(T* p, First&& first, Args&&... args)
{ 
  *p = first;
  push(++p, std::forward<Args>(args)...); 
}

那么,在你的班级里:

template<size_t sz,typename T=float>
class Vec
{
  T v[sz];  
  template <typename... Args>       
  Vec(T first, Args&&... args) // << we have changed const T& to T&&
  {
    //how do i assign `sz` number of first arguments into `this->v` array

    // like this:
    push(&v[0], first, std::forward<Args>(args)...);
  }
}

【讨论】:

    【解决方案2】:

    如果没有至少 sz 参数是可以的,这是另一种更简单的技术:

    template<size_t sz,typename T=float>
    struct Vec {
      T v[sz];
    
      template <typename... Args>
      Vec(const T& val,const Args&... nv)
      {
        T data[] = {val,static_cast<const T &>(nv)...};
        int i=0;
        for (; i<sz && i<(sizeof data)/sizeof(T); ++i) {
          v[i] = data[i];
        }
        for (; i<sz; ++i) {
          v[i] = T();
        }
      }
    };
    

    【讨论】:

      【解决方案3】:

      我相信这满足所有要求:

      template <size_t sz,typename T,typename... Args> struct Assign;
      
      template <typename T,typename First,typename...Rest>
      struct Assign<1,T,First,Rest...> {
        static void assign(T *v,const First &first,const Rest&... args)
        {
          *v = first;
        }
      };
      
      template <size_t sz,typename T,typename First,typename... Rest>
      struct Assign<sz,T,First,Rest...> {
        static void assign(T *v,const First &first,const Rest&... rest)
        {
          *v = first;
          Assign<sz-1,T,Rest...>::assign(v+1,rest...);
        }
      };
      
      template<size_t sz,typename T=float>
      struct Vec{
        T v[sz];
      
        template <typename... Args>
        Vec(const T& val,const Args&... nv){
          Assign<sz,T,T,Args...>::assign(v,val,nv...);
        }
      };
      

      【讨论】:

        【解决方案4】:

        以下几乎有效(并且对于 last N 个参数而不是第一个参数,但是嘿)。也许有人可以帮助解决以下 cmets 中的编译错误:

        #include <iostream>
        
        void foo (int a, int b) {
            std :: cout << "3 args: " << a << " " << b << "\n";
        }
        
        void foo (int a, int b, int c) {
            std :: cout << "3 args: " << a << " " << b << " " << c << "\n";
        }
        
        template <int n, typename... Args>
        struct CallFooWithout;
        
        template <typename... Args>
        struct CallFooWithout <0, Args...> {
            static void call (Args... args)
            {
                foo (args...);
            }
        };
        
        template <int N, typename T, typename... Args>
        struct CallFooWithout <N, T, Args...> {
            static void call (T, Args... args)
            {
                CallFooWithout <N-1, Args...> :: call (args...);
                // ambiguous class template instantiation for 'struct CallFooWithout<0, int, int, int>'
                // candidates are: struct CallFooWithout<0, Args ...>
                //                 struct CallFooWithout<N, T, Args ...>
            }
        };
        
        template <int n, typename... Args>
        void call_foo_with_last (Args... args)
        {
             CallFooWithout <sizeof...(Args)-n, Args...> :: call (args...);
        }
        
        int main ()
        {
            call_foo_with_last <2> (101, 102, 103, 104, 105);
            call_foo_with_last <3> (101, 102, 103, 104, 105);
        }
        

        我不明白为什么它是模棱两可的,因为 0 比 N 更专业,所以应该满足偏序?!?!?

        相比之下,下面的就好了。

        template <int N, typename... T>
        struct Factorial 
        {
            enum { value = N * Factorial<N - 1,T...>::value };
        };
        
        template <typename... T>
        struct Factorial<0, T...>
        {
            enum { value = 1 };
        };
        
        void foo()
        {
            int x = Factorial<4,int>::value;
        }
        

        有什么区别?

        【讨论】:

          【解决方案5】:

          这是可能的,但很复杂。这是一些执行此操作的代码。消除holder 类型是可能的,但我将其留给读者作为练习。这已经用 g++ 4.6 进行了测试。

          #include <iostream>
          #include <typeinfo>
          
          template<size_t ... Indices> struct indices_holder
          {};
          
          template<size_t index_to_add,typename Indices=indices_holder<> >
          struct make_indices_impl;
          
          template<size_t index_to_add,size_t...existing_indices>
          struct make_indices_impl<index_to_add,indices_holder<existing_indices...> >
          {
              typedef typename make_indices_impl<
                  index_to_add-1,
                  indices_holder<index_to_add-1,existing_indices...> >::type type;
          };
          
          template<size_t... existing_indices>
          struct make_indices_impl<0,indices_holder<existing_indices...> >
          {
              typedef indices_holder<existing_indices...>  type;
          };
          
          template<size_t max_index>
          typename make_indices_impl<max_index>::type make_indices()
          {
              return typename make_indices_impl<max_index>::type();
          }
          
          template<unsigned index,typename ... U>
          struct select_nth_type;
          
          template<unsigned index,typename T,typename ... U>
          struct select_nth_type<index,T,U...>
          {
              typedef typename select_nth_type<index-1,U...>::type type;
          
              static type&& forward(T&&,U&&... u)
              {
                  return select_nth_type<index-1,U...>::forward(static_cast<U&&>(u)...);
              }
          };
          
          template<typename T,typename ... U>
          struct select_nth_type<0,T,U...>
          {
              typedef T type;
          
              static type&& forward(T&&t,U&&...)
              {
                  return static_cast<T&&>(t);
              }
          };
          
          template<unsigned index,typename ... U>
          typename select_nth_type<index,U...>::type&& forward_nth(U&&... u)
          {
              return static_cast<typename select_nth_type<index,U...>::type&&>(
                  select_nth_type<index,U...>::forward(
                      static_cast<U&&>(u)...));
          }
          
          template<size_t sz,typename T=float> struct Vec{
              struct holder
              {
                  T data[sz];
              };
          
              holder v;
          
              template<typename ... U>
              struct assign_helper
              {
                  template<size_t... Indices>
                  static holder create_array(indices_holder<Indices...>,Vec* self,U&&... u)
                  {
                      holder res={{static_cast<T>(forward_nth<Indices>(u...))...}};
                      return res;
                  }
              };
          
              template<typename ... U>
              Vec(U&&... u):
                  v(assign_helper<U...>::create_array(make_indices<sz>(),this,static_cast<U&&>(u)...))
              {}
          };
          
          int main()
          {
              Vec<3> v(1.2,2.3,3.4,4.5,5.6,7.8);
          
              std::cout<<"v[0]="<<v.v.data[0]<<std::endl;
              std::cout<<"v[1]="<<v.v.data[1]<<std::endl;
              std::cout<<"v[2]="<<v.v.data[2]<<std::endl;
          }
          

          【讨论】:

            【解决方案6】:

            您需要在保持计数的同时解压参数包,并在函子中执行必要的运行时操作。这应该可以帮助您开始:

            template<unsigned, typename...>
            struct unroll;
            
            template<unsigned size, typename Head, typename... Tail>
            struct unroll<size, Head, Tail...> {
              void operator()(Head&& h, Tail&&... tail) {
                // do your stuff, pass necessary arguments through the ctor of the
                // struct
                unroll<size - 1, Tail...>()(std::forward<Tail>(tail)...);
              }
            };
            
            template<typename Head, typename... Tail>
            struct unroll<1, Head, Tail...> {
              void operator()(Head&& h, Tail&&... tail) {
                // do your stuff the last time and do not recurse further
              }
            };
            
            int main()
            {
              unroll<3, int, double, int>()(1, 3.0, 2);
              return 0;
            }
            

            【讨论】:

              【解决方案7】:

              你能用这样的东西吗?

              template <typename... Args> class Vec {
                  std :: tuple <Args...> m_args;
                  Vec (const Foo & a, const Bar & b, Args&&... args)
                  : m_args (args...)
                  {
                      // ... something with a, b
                  }
              };
              

              有一些规则可以约束你:

              • 每个模板类只能有一个template... Args 样式的打包参数列表
              • 使用它的方法必须在右侧具有模板化参数

              【讨论】:

              • std::pair&lt;Args...&gt; 有什么帮助? pair 正好有两个参数,为什么要让这个可变参数?
              • 哎呀,我的意思是tuple。谢谢。已更正。
              • 这个用起来还是挺麻烦的,因为需要指定所有的类模板参数。但是构造这样一个类的辅助函数会使它易于使用。
              【解决方案8】:

              以下方法可行:

              template <typename T, std::size_t N>
              struct Foo
              {
                T arr[N];
                template <typename ...Args> Foo(Args &&... args) : arr{std::forward<Args>(args)...} { }
              };
              

              用法:

              Foo<int, 3> a(1,2,3);
              

              这允许您从可转换为T 的任何内容构造数组元素。可以通过sizeof...(Args)获取参数个数(不超过N)。

              【讨论】:

              • 我的理解是Foo&lt;int, 3&gt; a(1,2,3,4); 也应该可以工作。 OP应该澄清。
              • 恐怕这是不可能的......好吧,至少没有相当多的帮助代码来跟踪参数的数量并且最多只返回N。跨度>
              • 是的,它是这样的,但最多只返回 N 个参数给数组
              • 我能想到的唯一解决方案是使用截断辅助类和委托构造函数。由于似乎还没有编译器支持委托构造函数,我不确定这会有多大帮助。
              • 顺便说一句,它对 TR1 有效吗?还是仅适用于 C++11?
              【解决方案9】:

              sz 是一个模板参数,你可以直接在你的代码中使用它。

              【讨论】:

              • OP在我回答后更改了问题。
              猜你喜欢
              • 2017-06-12
              • 1970-01-01
              • 2022-01-01
              • 1970-01-01
              • 2023-01-31
              • 1970-01-01
              • 1970-01-01
              • 2012-08-13
              • 1970-01-01
              相关资源
              最近更新 更多