Конструктор шаблонного класса C++ с аргументами переменной

Действительно ли возможно создать шаблонную функцию, которая берет переменное количество аргументов, например, в этом 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 во время вызова функции, и что другие интерпретируются как ints и затем биты наполнены в a double. Вызов Vector< double, 3 >( 1.0, 1.0, 1.0 ) дает желаемые результаты. Существует ли предпочтительный способ сделать что-то вроде этого?

10
задан Ziezi 21 September 2015 в 08:56
поделиться

6 ответов

Этот код выглядит опасным, и я думаю, что ваш анализ того, почему он не работает, точен, компилятор не может знать, что при вызове:

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);
2
ответ дан 3 December 2019 в 23:49
поделиться

Увы, сейчас нет хорошего способа сделать это. Большинство пакетов 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 лет, в течение которых многие компиляторы будут поддерживать стандарт лишь частично, поэтому ваш код не будет очень переносимым.

9
ответ дан 3 December 2019 в 23:49
поделиться

Вы можете делать то, что хотите, но не делайте этого, потому что это небезопасно для типов. Лучше всего передавать вектор 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 достаточно места.

2
ответ дан 3 December 2019 в 23:49
поделиться

В 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&);
// ...

Поскольку описанное выше довольно болезненно выполнять вручную, вы можете написать сценарий для его генерации.

0
ответ дан 3 December 2019 в 23:49
поделиться

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));
0
ответ дан 3 December 2019 в 23:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: