5

I'm writing a container storage class template that wraps a private std::array in order to add some functionality to it. The template parametrises the number of values, as follows:

template<size_t N> class Vector {
private:
    array<double, N> vals;
public:
    [...]
};

I'd like the constructor for the class to only accept N doubles to fill the array, but I can't find a good way to do this. Variadic arguments don't provide a mechanism to check how many of them there are, so they're right out. Parameter packs don't do floating-point promotion, but I'd be willing to deal with that if I could only figure out how to use them for this.

I've tried following the approach in the answer to Member function template with the number of parameters depending on an integral template parameter but I can't understand the significance enable_if<>::type=0 section. I've tried naïvely copying that code in (though I'd much rather understand how it works. I've seen people use ::value in other places but I can't find any documentation on why) but expanding the resulting parameter pack doesn't seem to work. My other concern with parameter packs is that I'm not sure that they'd ensure the types of all arguments were the same.

I've tried running a static_assert on the size of an initializer list, in the body of the constructor, but of course the size of the list is not constant at compile time, so that doesn't work either.

Is there a standard approach here? Am I just using parameter packs wrong?


Update: I've got the approach in the answer I linked above partly working:
template<size_t N> class Vector {
private:
    array<double, N> vals;
public:
    template <typename ...T,
          typename enable_if<sizeof...(T) == N, int>::type = 0>
    Vector(T ...args) {
        vals = {args...};
    }
};

The issue is now that the enable_if term in the template means that when I initialise a Vector with, for example,

Vector<3> V {1.0, 2.0, 3.0};

It requests a template specialisation Vector<3>::Vector<double, double, double, 0> rather than <double, double, double>. How do I get rid of this stray term in the template?

Community
  • 1
  • 1
TroyHurts
  • 337
  • 1
  • 11

3 Answers3

6

Don't get what you mean by this:

Variadic arguments don't provide a mechanism to check how many of them there are, so they're right out

template <typename ...T>
Vector(T... args) {
    static_assert(sizeof...(args) <= N, "oops");
}

Should work..

Nim
  • 33,299
  • 2
  • 62
  • 101
  • 2
    Nice. I didn't know about [`sizeof...`](http://en.cppreference.com/w/cpp/language/sizeof...) – wally Mar 21 '16 at 13:13
  • 1
    That yields `'args' does not refer to the name of a parameter pack`. I don't think sizeof expansion applies to old-fashioned variadic arguments and parameter packs in the same way. Aside from that issue, will static_asserts correctly prevent that overload from being selected, or will they just get stuck if the assert fails? – TroyHurts Mar 21 '16 at 13:14
  • 1
    @TroyHurts `static_asserts` won't prevent that overload from being selected, for that you should use `std::enable_if` as explained in the post you linked. – Holt Mar 21 '16 at 13:15
  • why do you limit the number of parameters to `N-1` ? – Piotr Skotnicki Mar 21 '16 at 13:21
  • @Holt have tried further to get `enable_if` working. See above. – TroyHurts Mar 21 '16 at 13:25
  • Shouldn't it be `sizeof...(args)`? – AndyG Mar 21 '16 at 13:27
2

You could additionally generate a pack of the right size via some template specialization tricks:

template <size_t N, class = std::make_index_sequence<N>>
class Vector;

template <size_t N, size_t... Is>
class Vector<N, std::index_sequence<Is...>> 
{
private:
    std::array<double, N> vals;

    template <size_t >
    using double_ = double;
public:
    Vector(double_<Is>... vals)
    {
        ...
    }
};

That is a non-template constructor which takes N doubles.

Barry
  • 286,269
  • 29
  • 621
  • 977
2

By following Member function template with the number of parameters depending on an integral template parameter and Variadic templates with exactly n parameters, have got it working with the following code.

template<size_t N> class Vector {
private:
    array<double, N> vals;
public:
    template <typename ...T,
          typename enable_if<sizeof...(T) == N, int>::type = 0>
    Vector(T ...args) : vals{args} {}
};
Community
  • 1
  • 1
TroyHurts
  • 337
  • 1
  • 11
  • you could make it even *more* sfinaebla by adding another parameter like `typename = decltype(std::array{{std::declval()...}})` – Piotr Skotnicki Mar 21 '16 at 16:07