Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class method with number of arguments specified by integer template parameter

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.

like image 784
bpw1621 Avatar asked Aug 11 '26 08:08

bpw1621


2 Answers

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.

like image 156
Matthieu M. Avatar answered Aug 15 '26 01:08

Matthieu M.


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.

like image 45
phlipsy Avatar answered Aug 15 '26 01:08

phlipsy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!