Фабричный метод, возвращающий конкретную реализацию шаблона класса C ++ - PullRequest
1 голос
/ 31 января 2009

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

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

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

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

Ответы [ 4 ]

1 голос
/ 31 января 2009

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

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

Итак, вы хотите, чтобы в шаблоне (и, следовательно, во всех его экземплярах) была функция, возвращающая 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 ), то вам нужна специализация шаблона:


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

0 голосов
/ 31 января 2009

@ LITB

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

0 голосов
/ 31 января 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 ::. Это не стоит того, я думаю. В следующей версии 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);

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

0 голосов
/ 31 января 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 на базовый массив, но он будет использовать необработанные массивы на свой страх и риск:)

...