Метод класса с количеством аргументов, заданным целочисленным параметром шаблона

Не был точно уверен, как сформулировать этот вопрос или что искать, поэтому, если это то же самое, что и другой вопрос, пожалуйста, закройте и перенаправьте на соответствующий вопрос.

Предполагать

template<typename Type, int Size> class vector
{
  Type data[Size];
}

Можно ли заменить конструктор, который принимает количество аргументов Size в специализациях шаблона, подобных этому

template<typename Type> class vector3<Type,3>
{
  Type data[3];
  public:
    vector3( Type, Type, Type );
}

с чем-то в неспециализированном классе шаблонов? Как «конструктор varargs», который создает конструктор с количеством аргументов Size типа Type?

Решение, включающее функции C++0x, прекрасно.


person bpw1621    schedule 15.07.2010    source источник


Ответы (4)


В C++0x наконец-то доступно template typedef!

Отказ от ответственности: ничего не скомпилировано...

Из статьи Википедии:

template< typename second>
using TypedefName = SomeType<OtherType, second, 5>;

что в вашем случае даст

template <class Type>
using vector3 = vector<Type, 3>;

Я не могу передать вам, как сильно я жаждал этого ;)

Однако это не решает проблему параметров. Как уже упоминалось, вы можете попробовать использовать вариативные шаблоны здесь, однако я не уверен в их применении в этом случае. Нормальное использование - с рекурсивными методами, и вам нужно будет бросить static_assert посредине.

Отредактировано с учетом комментариев.

template <class Type, size_t Size>
class vector
{
public:
  template <class... Args>
  vector(Args... args): data({args...})
  {
    // Necessary only if you wish to ensure that the exact number of args
    // is passed, otherwise there could be less than requested
    BOOST_MPL_ASSERT_RELATION(sizeof...(Args), ==, Size);
  }

private:
  T data[Size];
};

Еще одна возможность, которая уже доступна, — объединить генерацию препроцессора с boost::enable_if.

template <class Type, size_t Size>
class vector
{
public:
  vector(Type a0, typename boost::enable_if_c< Size == 1 >::type* = 0);
  vector(Type a0, Type a1, typename boost::enable_if_c< Size == 2 >::type* = 0);
  // ...
};

Использование Boost.Preprocessor для генерации упрощает эту задачу.

BOOST_PP_REPEAT(MAX_COUNT, CONSTRUCTOR_MACRO, ~);

// where MAX_COUNT is defined to the maximum size you wish
// and CONSTRUCTOR_MACRO actually generates the constructor

#define CONSTRUCTOR_MACRO(z, n, data)                              \
  vector(                                                          \
    BOOST_PP_ENUM_PARAMS(n, Type a),                               \
    typename boost::enable_if_c< Size == n >::type* = 0            \
  );

Реализация конструктора оставлена ​​читателю в качестве упражнения. Это еще один звонок BOOST_PP_REPEAT.

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

person Matthieu M.    schedule 15.07.2010
comment
sizeof...(Args) для количества аргументов шаблона? - person Tomek; 15.07.2010
comment
Избавьтесь от своей функции init, используя список инициализаторов: data({args...}) - person phlipsy; 16.07.2010

Есть еще одно решение вашей проблемы: использование вариативных параметров шаблона в списке инициализаторов.

template<typename T, unsigned int N>
struct vector {
    T data[N];

    template<typename... Args>
    vector(Args... args) : data({args...}) { }
};

Однако количество аргументов должно быть меньше или равно N, а их типы должны быть преобразованы только в T.

person phlipsy    schedule 15.07.2010
comment
Это работает и для меня, я думаю, моя формулировка звучала так, как будто это было требованием. Я отредактирую, чтобы сказать по крайней мере. В этом случае конструктор должен по умолчанию создавать другие члены (если они есть), верно? - person bpw1621; 16.07.2010
comment
@bpw1621: Верно. Но, как сказал Каспин, вам следует рассмотреть возможность использования array также для предоставления массива фиксированной длины. Однако, если вам, например, нужно перегрузить арифметические операции для вашего типа, вы будете вынуждены написать свой собственный новый класс, в идеале используя внутренне array. - person phlipsy; 16.07.2010
comment
лол, я написал тонну кода, чтобы сделать это, прежде чем увидел этот ответ. :П - person CodeRarity; 14.02.2013

Можно ли заменить конструктор, который принимает количество аргументов Size в специализациях шаблона, подобных этому

Не обошлось и без тонны повторяющегося механического кода, а максимальный размер будет ограничен количеством повторений. Показательный пример: boost::tuple (который может иметь именно ту функциональность, которую вы хотите).

В C++0x это не будет проблемой благодаря вариативным шаблонам.

person Cogwheel    schedule 15.07.2010

Сначала вы должны рассмотреть возможность использования std::array. Он не отвечает всем вашим требованиям, но достаточно близок, чтобы, если различия не имеют значения, вы могли сэкономить себе много работы. Проблема в том, что эта дешевая версия будет иметь конструкцию, которая принимает 2 аргумента, а не 3.

template< typename T>
using Vector3 = std::array<T,3>;

Vector3 v1{1,2,3};
Vector3 v2{1,2}; // it sounds like you want to disallow this case.

В противном случае вы можете создать собственный класс, который во многом похож на std::array, за исключением более привередливого конструктора.

template<typename T, std::size_t SIZE>
class Vector
{
public:
   template< typename ... Args >
   Vector( Args ... args ) :
      data({args...})
   {
      static_assert( sizeof...(Args) == SIZE,
                     "Incorrect number of arguments passed to Vector constructor");
   }
   /* lots of extra code here to add std::array -like methods */
private:
   // could use std::array here as well.
   T data[3];
};

template< typename T >
using Vector3 = Vector<T,3>;

Vector3 v1(1,2,3);
Vector3 v2(1,2); // error at compile time.
person deft_code    schedule 15.07.2010
comment
Хорошая идея использовать static_assert, чтобы гарантировать точное количество аргументов. Есть идеи по поводу их типов? Они должны быть конвертированы только в T... - person phlipsy; 16.07.2010
comment
Я мог бы добавить еще один static_assert, используя std::same_type<T,U>, чтобы обеспечить правильный тип, но я думаю, что это было бы саморазрушением. Я бы хотел, чтобы Vector3<unsigned> vi(11,12,13) просто работал. Если мне нужны точные типы, мне придется добавить беззнаковые спецификаторы к целочисленным литералам. - person deft_code; 16.07.2010