Заводской метод, возвращающий конкретный экземпляр класса шаблона C ++

У меня есть класс

template <unsigned int N>
class StaticVector {
// stuff
};

Как я могу объявить и определить в этом классе статический фабричный метод, возвращающий объект StaticVector ‹3>, например

StaticVector<3> create3dVec(double x1, double x2, double x2);

?


person quant_dev    schedule 31.01.2009    source источник
comment
В своем примере вы должны заменить public классом.   -  person Klaim    schedule 31.01.2009


Ответы (4)


«Как я могу объявить и определить в этом классе»

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

Итак, хотите ли вы, чтобы в шаблоне (и, следовательно, во всех его экземплярах) была функция, возвращающая StaticVector ‹3›, или вы хотите, чтобы в одном конкретном экземпляре этого шаблона была функция, возвращающая StaticVector ‹3›?

Если первое:


  template <unsigned int N>
  struct SV {
    int contents[N];
    static SV<3> get3dVec(int x, int y, int z) {
      SV<3> v;
      v.contents[0] = x;
      v.contents[1] = y;
      v.contents[2] = z;
      return v;
    }
  };

  int main() {
    SV<3> v = SV<1>::get3dVec(1,2,3);
  }

работает на меня.

Если последнее (вы хотите, чтобы get3dVec был участником SV ‹3›, а не всех SV ‹whatever›), тогда вам нужна специализация шаблона:


  template <unsigned int N>
  struct SV {
    int contents[N];
  };

  template<>
  struct SV<3> {
    int contents[3]; // must be re-declared in the specialization
    static SV<3> get3dVec(int x, int y, int z) {
      SV<3> v;
      v.contents[0] = x;
      v.contents[1] = y;
      v.contents[2] = z;
      return v;
    }
  };

  int main() {
    SV<3> v = SV<1>::get3dVec(1,2,3); // compile error
    SV<3> v = SV<3>::get3dVec(1,2,3); // OK
  }

Если только для того, чтобы вызывающий код выглядел лучше, опуская в основном нерелевантный параметр шаблона, я согласен с Iraimbilanja, что обычно бесплатная функция (в пространстве имен, если вы пишете для повторного использования) будет иметь больше смысла для этого пример.

Шаблоны C ++ означают, что вам не нужны статические функции в C ++ так, как в Java: если вам нужна функция "foo", которая выполняет одно действие для класса Bar, а другое - для класса Baz, вы можете объявить ее как функцию шаблон с параметром шаблона, который может быть Bar или Baz (и который может быть или не может быть выведен из параметров функции), вместо того, чтобы делать его статической функцией для каждого класса. Но если вы хотите, чтобы это была статическая функция, вы должны вызывать ее, используя определенный класс, а не только имя шаблона.

person Steve Jessop    schedule 31.01.2009

Что-то типа :

template< unsigned int SIZE >
StaticVector< SIZE > createVec( const std::tr1::array< double, SIZE >& values )
{ 
     StaticVector< SIZE > vector;
     for( int i = 0; i < values.size(); ++i ) // here assuming that StaticVector have [] operator to access values on write
     {
         vector[i] = values[i];
     }

     return vector;
}

... или вариант, безусловно, подойдет. (не тестировал)

Использование будет:

std::tr1::array< double, 3 > vectorValues = { 10.0, 10.0, 42.0 };

StaticVector<3> vector1 = createVector( vectorValues ); // if the compiler can guess the size from the array information
StaticVector<3> vector2 = createVector<3>( vectorValues ); // if you have to specify the size

Альтернативой была бы замена std :: tr1 :: array базовым массивом, но при этом использовались бы необработанные массивы на свой страх и риск :)

person Klaim    schedule 31.01.2009

Во-первых, я считаю, что вы изначально собирались вернуться

StaticVector<N>

Вместо того, чтобы всегда иметь специализацию с N == 3. Итак, что вы хотите сделать, это написать это так:

template <unsigned int N>
class StaticVector {
public:
    // because of the injected class-name, we can refer to us using
    // StaticVector . That is, we don't need to name all template
    // parameters like StaticVector<N>.
    static StaticVector create3dVec(double x1, double x2, double x2) {
        // create a new, empty, StaticVector
        return StaticVector();
    }

};

Если вы действительно хотите всегда возвращать 3dVector, вы, вероятно, захотите ограничить его значением N == 3, чтобы, например, StaticVector<4>::create3dVec не работал. Вы можете сделать это, используя описанную технику здесь.

Если вы хотите иметь такую ​​функцию, как createVec, которая работает с любым размером, вы, вероятно, захотите заменить параметры массивом. Вы можете поступить иначе, но это продвинуто и требует некоторых макросов, применяемых с помощью boost :: preprocessor. Думаю, это того не стоит. Следующая версия C ++ предоставит для этой цели вариативные шаблоны. В любом случае, подумайте об использовании чего-то вроде этого:

Думаю, здесь это только излишне усложнило бы. Быстрое решение - вместо этого использовать boost :: fusion :: vector, поместив его в шаблон класса вместо версии, указанной выше:

static StaticVector createVec(double (&values)[N]) {
    // create a new, empty, StaticVector, initializing it
    // with the given array of N values.
    return StaticVector();
}

Вы можете использовать это с

double values[3] = {1, 2, 3};
StaticVector<3> v = StaticVector<3>::createVec(values);

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

// oops, values is a null pointer!
StaticVector<3> v = StaticVector<3>::createVec(values);

Массив никогда не может быть нулевым указателем. Конечно, если хотите, вы всегда можете изменить параметр массива на указатель. Это было бы моим личным предпочтением :)

person Johannes Schaub - litb    schedule 31.01.2009

@litb

Я хотел вернуть трехэлементный вектор. Причина в том, что эти векторы должны быть геометрическими объектами, поэтому нет необходимости в том, чтобы N было слишком большим (я не физик-струнник, поэтому я не работаю в 11-мерных пространствах). Я хотел создать шаблон, чтобы избежать дублирования кода, но сохранить 1-, 2- и 3-мерные векторы как отдельные типы.

person quant_dev    schedule 31.01.2009
comment
Если определенные измерения являются особыми случаями, вам может быть полезно выполнить либо typedef StaticVector ‹3› Vec3; или класс Vec3: public StaticVector ‹3› {. Первый просто напоминает вам, что он особенный. Последний позволяет вам определять специальные свойства Vec3, но на всякий случай оставлять StaticVector ‹3› в покое. - person Steve Jessop; 01.02.2009
comment
... на всякий случай, если вам когда-нибудь понадобится StaticVector ‹3›, который не имеет особого поведения, я имею в виду. - person Steve Jessop; 01.02.2009