связи c первых аргументов функции N вариативная
у меня есть следующий код :
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<3> var(1.0,2.0,3.0);
9 ответов
это возможно, но сложно. Вот код, который это делает. Возможно, удастся устранить 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;
}
Я считаю, что это удовлетворяет всем требованиям:
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...);
}
};
это еще один метод, который намного проще, если нормально не иметь хотя бы 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();
}
}
};
сначала объявите эту функцию утилиты:
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)...);
}
}
может работать следующее:
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
. Вы можете получить количество параметров (которое может быть чем угодно, не превышающим N
) С sizeof...(Args)
.
можете ли вы использовать что-то подобное?
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
- style упакованный список аргументов для каждого класса шаблона - методы, которые используют его, должны иметь шаблонные аргументы справа
вам нужно распаковать пакет аргументов, сохраняя счетчик, и выполнить необходимую операцию выполнения в функторе. Это должно заставить вас начать:
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;
}
следующее почти работает (и для последние N аргументов вместо первого, но эй). Возможно, кто-то может помочь с ошибкой компиляции в комментариях ниже:
#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;
}
в чем разница?