Действительно ли возможно создать шаблонную функцию, которая берет переменное количество аргументов, например, в этом Vector< T, C >
конструктор класса:
template < typename T, uint C >
Vector< T, C >::Vector( T, ... )
{
va_list arg_list;
va_start( arg_list, C );
for( uint i = 0; i < C; i++ ) {
m_data[ i ] = va_arg( arg_list, T );
}
va_end( arg_list );
}
Это почти работает, но если кто-то звонит Vector< double, 3 >( 1, 1, 1 )
, только первый аргумент имеет правильное значение. Я подозреваю, что первый параметр корректен, потому что он брошен к a double
во время вызова функции, и что другие интерпретируются как int
s и затем биты наполнены в a double
. Вызов Vector< double, 3 >( 1.0, 1.0, 1.0 )
дает желаемые результаты. Существует ли предпочтительный способ сделать что-то вроде этого?
Этот код выглядит опасным, и я думаю, что ваш анализ того, почему он не работает, точен, компилятор не может знать, что при вызове:
Vector< double, 3 >( 1, 1, 1 )
те должны быть сданы как двойные.
Вместо этого я бы изменил конструктор на что-то вроде:
Vector< T, C >::Vector(const T(&data)[C])
и попросил бы пользователя передавать аргументы в виде массива. Другой вид уродливого решения может быть примерно таким:
template < typename T, uint C >
Vector< T, C >::Vector(const Vector<T, C - 1>& elements, T extra) {
}
и называть его так (с некоторыми определениями типов):
Vector3(Vector2(Vector1(1), 1), 1);
Увы, сейчас нет хорошего способа сделать это. Большинство пакетов Boost, которые должны делать что-то подобное, используют макросы для определения таких вещей, как это:
template < typename T >
Vector< T >::Vector( T )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1 )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2 )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2, C c3 )
{ ... }
Макросы генерируют некоторое заданное количество (обычно около 10) версий и предоставляют механизм для изменения максимального количества параметров перед расширением строительство.
По сути, это настоящая боль, поэтому C ++ 0x вводит аргументы шаблона переменной длины и методы делегирования, которые позволят вам делать это чисто (и безопасно). А пока вы можете сделать это с помощью макросов или попробовать компилятор C ++, который поддерживает (некоторые из) этих новых экспериментальных функций. GCC подходит для этого.
Имейте в виду, что, поскольку C ++ 0x еще не вышел, все еще может измениться, и ваш код может не синхронизироваться с окончательной версией стандарта. Кроме того, даже после того, как стандарт выйдет, пройдет около 5 лет, в течение которых многие компиляторы будут поддерживать стандарт лишь частично, поэтому ваш код не будет очень переносимым.
Вы можете делать то, что хотите, но не делайте этого, потому что это небезопасно для типов. Лучше всего передавать вектор T
или пару итераторов, содержащих эти значения.
template < typename T, uint C >
Vector< T, C >::Vector(int N, ... )
{
assert(N < C && "Overflow!");
va_list arg_list;
va_start(arg_list, N);
for(uint i = 0; i < N; i++) {
m_data[i] = va_arg(arg_list, T);
}
va_end(arg_list);
}
Vector<int> v(3, 1, 2, 3);
Это можно решить лучше, поскольку все элементы в любом случае однородно типизированы.
template < typename Iter, uint C >
Vector< T, C >::Vector(Iter begin, Iter end)
{
T *data = m_data;
while(begin != end)
*data++ = *begin++;
}
int values[] = { 1, 2, 3 };
Vector<int> v(values, values + 3);
Конечно, нужно убедиться, что в m_data
достаточно места.
В C ++ 0x (на самом деле его следует называть C ++ 1x), вы можете использовать шаблонные varargs для достижения желаемого типобезопасным способом (и вам даже не нужно указывать количество аргументов!). Однако в текущей версии C ++ (ISO C ++ 1998 с поправками 2003 г.) нет возможности выполнить то, что вы хотите. Вы можете либо отложить, либо сделать то, что делает Boost, то есть использовать макросы препроцессора для многократного повторения определения конструктора с различным количеством параметров, вплоть до жестко запрограммированного, но большого предела. Учитывая, что Boost.Preprocessor является своего рода сложным, вы могли бы просто определить все следующее самостоятельно:
Vector<T,C>::Vector(); Vector<T,C>::Vector(const T&); Vector<T,C>::Vector(const T&, const T&); // ...
Поскольку описанное выше довольно болезненно выполнять вручную, вы можете написать сценарий для его генерации.
std :: tr1 :: array
(который похож на ваш) не определяет конструктор и может быть инициализирован как агрегат (? )
std::tr1::array<int, 10> arr = {{ 1, 2, 3, 4, 5, 6 }};
Также вы можете ознакомиться с библиотекой Boost.Assignment .
Например, конструктор может быть
template < typename T, uint C >
template < typename Range >
Vector< T, C >::Vector( const Range& r )
, а экземпляры созданы с помощью
Vector<int, 4> vec(boost::assign::cref_list_of<4>(1)(3)(4)(7));