开发者

accessing first n variadic function arguments

开发者 https://www.devze.com 2023-04-07 07:46 出处:网络
I have the following code : template<size_t sz,typename T=float> class Vec{ T v[sz]; Vec(const T& val,const T&... nv){

I have the following code :

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` 开发者_StackOverflow中文版array
    }
}

I want to create constructor, that receive generic number of constructor argument, and assign the first sz number of arguments into member variable of v

what I want to do, is to be able doing like this: Vec<3> var(1.0,2.0,3.0);


This is possible, but complicated. Here is some code that does it. It may be possible to eliminate the holder type, but I leave that as an exercise for the reader. This has been tested with 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;
}


I believe this satisfies all the requirements:

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...);
  }
};


This is another technique which is much simpler if it is ok not to have at least sz parameters:

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();
    }
  }
};


First declare this utility function:

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)...); 
}

Then, in your class:

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)...);
  }
}


sz is a template argument, you can directly use it in your code.


The following could work:

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

Usage:

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

This allows you to construct the array elements from anything that's convertible to T. You can obtain the number of parameters (which can be anything not exceeding N) with sizeof...(Args).


Can you make use of something like this?

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
    }
};

There are a few rules to constrain you:

  • you can only have one template... Args-style packed argument list per template class
  • methods which use it must have the templated arguments on the right-hand-side


You need to unpack the argument pack while keeping a count and do the necessary runtime operation in a functor. This should get you started:

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;
}


The following almost works (and for the last N arguments instead of the first, but hey). Perhaps someone can help with the compile error in the comments below:

#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);
}

I don't see why it's ambiguous because 0 is more specialised than N so that should satisfy the partial order ?!?!?

By contrast, the below is fine.

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;
}

What's the difference?

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号