Constructors that take different sizes of arrays

Viewed 90

I can do this:

struct foo{
    foo(std::array<double, 3>){}
    foo(std::array<double, 4>){}
}

I would like to do this:

struct foo{
    foo(double A[3]){}
    foo(double A[4]){}
}

Which, of course, does not work.

This involves legacy code with a lot of 3 and 4 element double arrays. I desire to construct a class that contains 4 doubles from either 3 or 4 element arrays, and initialize the 4th double with a constant value when constructing from a 3 element array. When I initialize from a 4 element array, I just copy in the 4 elements.

So, I would like to make constructor(s) to recognize:

double something[3];

from:

double something_else[4];

The best I can think of is to add another parameter to the constructor to distinguish between the two.

class foo{
    foo(double A[4], bool only_3 = false){}

    foo(double *A, size_t n = 4){}
}

Any better ideas?

(As time permits, I'll chase out all the raw arrays, but for now I have to deal with it.)

2 Answers

Arrays can be passed by reference. If you have

struct foo{
    foo(double (&A)[3]){}
    foo(double (&A)[4]){}
};

Then foo can only be constructed from an array of size 3 or size 4. This does stop you from accepting pointers though so

double bar = new double[4];
foo f(bar);

wont work.

You can make your constructors accept references to the arrays:

struct foo {
    foo(const double (&A)[3]) {}
    foo(const double (&A)[4]) {}
};

And if the functions are similar enough, you could make a template accepting both versions:

struct foo {
    template<size_t N, std::enable_if_t<N == 3 || N == 4, bool> = true>
    constexpr foo(const double (&A)[N]) noexcept {
        std::copy(std::begin(A), std::end(A), values);
        if constexpr(N == 3) values[3] = ... // some constant;
    }

    double values[4];
};

If you need to accept dynamically allocated arrays (double*) with sizes known at compile time, you could create a constructor template using tags to assert that the sizes used are acceptable.

struct foo {
    template<size_t N, std::enable_if_t<N == 3 || N == 4, bool> = true>
    struct size_tag_t {};

    // convenience tag instances
    static constexpr size_tag_t<3> three_tag{};
    static constexpr size_tag_t<4> four_tag{};

    template<size_t N>
    constexpr foo(const double* A, size_tag_t<N>) noexcept {
        std::copy(A, A + N, values);
        if constexpr(N == 3) values[3] = ... // some constant;
    }

    double values[4];
};

//...
constexpr size_t THREE = 3;
double* da3 = new double[THREE];
foo pthree1(da3, foo::size_tag_t<THREE>{});
// or
foo pthree2(da3, foo::three_tag);
Related