views:

94

answers:

4

Was not exactly sure how to phrase this question or what to search on so if this is the same as another question please close and redirect to the appropriate question.

Suppose

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

Is it possible to replace a constructor which takes Size number of arguments in template specializations like this

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

with something in the non-specialized template class? Like a "varargs constructor" that produces a constructor with Size number of arguments of type Type?

A solution involving C++0x features is fine.

+2  A: 

Is it possible to replace a constructor which takes Size number of arguments in template specializations like this

Not without a ton of repetitive, mechanical code, and the maximum size will be limited by the number of times you repeat yourself. Case in point: boost::tuple (which might have exactly the functionality what you want).

In C++0x this won't be a problem thanks to variadic templates.

Cogwheel - Matthew Orlando
+5  A: 

In C++0x you have template typedef finally available!

Disclaimer: nothing has been compiled...

From Wikipedia's article:

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

which in your case would yield

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

I can't tell you how much I craved for this ;)

However it doesn't solve the parameters issue. As mentioned, you could try and use variadic templates here, however I am unsure as to their application in this case. The normal use is with recursive methods and you would need to throw a static_assert in the midst.

Edited to take the comments into account.

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

Another possibility that is already available is to combine Preprocessor generation with 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);
  // ...
};

Using Boost.Preprocessor for the generation makes this easier.

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

The implementation of the constructor is left as an exercise for the reader. It's another call to BOOST_PP_REPEAT.

As you can see, it soon gets ugly, so you'll be better off if you can use the variadic template version.

Matthieu M.
sizeof...(Args) for number of template args?
Tomek
Perhaps, I have never needed it yet and I can't find any reference...
Matthieu M.
Get rid of your `init` function by using the initializer list: `data({args...})`
phlipsy
+2  A: 

There's a further solution to your problem: Using variadic template parameters in the initializer list

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

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

However the number of arguments only needs to be less or equal than N and their types only needs to be convertible to T.

phlipsy
That works for me too I guess my wording sounded like "exact" was a requirement. I'll edit to say at least. The constructor should default construct the other members (if there are any) in that case, right?
bpw1621
@bpw1621: Right. But as Caspin told you should consider using `array` also providing an array of fixed length. However if you for example need to overload arithmetic operations for your type you'll be forced to write your own new class ideally using internally `array`.
phlipsy
+2  A: 

First you should consider using std::array. It doesn't meet all your requirements but it's close enough that if the differences don't matter you can save yourself a lot of work. The problem is this cheap version will have construct that accepts 2 arguments as well as 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.

Otherwise you can create a custom class that acts a lot like std::array, except with a more picky constructor.

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.
caspin
Nice idea to use static_assert to guarantee the exact number of arguments. Any ideas concerning their types? They only have to be convertible to `T`...
phlipsy
I could add another `static_assert` using `std::same_type<T,U>` to enforce the correct type, but I think that would be self defeating. I would like `Vector3<unsigned> vi(11,12,13)` to just work. If I require exact types I'd have to add the unsigned specifiers to the integer literals.
caspin