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

StackOverflow https://stackoverflow.com/questions/498757

  •  20-08-2019
  •  | 
  •  

Вопрос

У меня есть занятие

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

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

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

?

Это было полезно?

Решение

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

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

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

Другие советы

Что - то вроде :

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 базовым массивом, но это означало бы использование необработанных массивов на свой страх и риск :)

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

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);

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

@литб

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top