views:

682

answers:

5

I have a class

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

How can I declare and define in this class a static factory method returning a StaticVector<3> object, sth like

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

?

A: 

Something like :

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

... or a variant would certainly work. (did'nt test it)

Usage would be:

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

An alternative would be to replace std::tr1::array by a basic array but it would be using raw arrays at your own risks :)

Klaim
+1  A: 

"How can I declare and define in this class"

In what class? You've defined a class template, not a class. You can't call a static function of a class template itself, you have to call a particular version of the static function that's part of a real class.

So, do you want the template (and hence all instantiations of it) to have a function returning a StaticVector<3>, or do you want one particular instantiation of that template to have a function returning a StaticVector<3>?

If the former:


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

works for me.

If the latter (you only want get3dVec to be a member of SV<3>, not of all SV<whatever>), then you want template specialisation:


  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
  }

If for no other reason than to make the calling code look nicer by omitting the basically irrelevant template parameter, I agree with Iraimbilanja that normally a free function (in a namespace if you're writing for re-use) would make more sense for this example.

C++ templates mean that you don't need static functions as much in C++ as you do in Java: if you want a "foo" function that does one thing for class Bar and another thing for class Baz, you can declare it as a function template with a template parameter that can be Bar or Baz (and which may or may not be inferred from function parameters), rather than making it a static function on each class. But if you do want it to be a static function, then you have to call it using a specific class, not just a template name.

Steve Jessop
A: 

Thanks guys for all answers. Yes, I code for living in Java and it shows in how I've approached this problem ;-)

quant_dev
A: 

First, i believe you originally mean to return

StaticVector<N>

Instead of always a specialization with N==3 . So, what you want to do is writing it like this:

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

};

If you really want to always return a 3dVector, you would probably want to restrict it to N==3, so that for example StaticVector<4>::create3dVec doesn't work. You can do that using the technique described here.

If you want to have a function like createVec that works with any size, you probably want to replace the parameters with an array. You can do otherwise, but that's advanced and requires some macro tricks applied with boost::preprocessor. It's not worth it i think. The next C++ version will provide variadic templates for this purpose. Anyway,consider using something like this:

I think it would only complicate this unnecessarily here. A quick solution is to use a boost::fusion::vector instead, putting it into the class template instead of the version above:

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

You could use it with

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

Note that it accepts an array by reference. You can't give it a pointer. That is because it matches the use of parameters: You couldn't provide less or more arguments for the other way too. It will also protect you from cases like this:

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

An array never can be a null pointer. Of course if you like, you can always change the array parameter to a pointer. It would just be my personal preference :)

Johannes Schaub - litb
A: 

@litb

I wanted to return a 3-element vector. The reason is that these vectors are supposed to be geometric entities, so there is no need for N to be too high (I'm not a string physicist, so I don't work in 11-dimensional spaces). I wanted to create a template to avoid duplicating code but keep 1-, 2- and 3-dimensional vectors as separate types.

quant_dev
If certain dimensions are special cases, you might find it helpful to do either "typedef StaticVector<3> Vec3;" or "class Vec3 : public StaticVector<3> {". The former just reminds you it's special. The latter lets you define "special" properties of Vec3 but leave StaticVector<3> alone just in case.
Steve Jessop
... just in case you ever actually want a StaticVector<3> that doesn't have the special-case behaviour, I mean.
Steve Jessop