Eissa N.
Eissa N.

Reputation: 1725

Passing C-arrays of different sizes to templated function taking 2 identical parameters

I have a templated function func as follows which takes two parameters of the same type (usually two STL containers of the same type but different sizes). I want to make it work with C-arrays of the same type but different sizes too.

template<class T>
void func(const T& a, const T& b) {
    // some code like as follows
    for(auto x : a) {
        cout << x << " ";
    }
    cout << endl;
    for(auto x : b) {
        cout << x << " ";
    }
}

Obviously, the following code fails with error: no matching function for call to 'func(int [2], int [3])':

int a1[] = {1, 2};
int a2[] = {3, 4, 5};
func(a1, a2);

I cannot alter the function signature but I can overload it. I also want to avoid unnecessary copies too. My attempt was to write an overload like:

template<class T, size_t M, size_t N>
void func(const T (&a)[M], const T (&b)[N]) {
    //somehow calling f<T>(const T&, const T&) without copying array elements
}

But, I am not sure how to implement it. Any ideas are welcome. Thanks!

Live demo based on this answer.

Upvotes: 3

Views: 96

Answers (3)

M.M
M.M

Reputation: 141554

One option is to use forwarding references:

template<class T, class U>
void func(T&& a, U&& b)
{
    for(auto x : a)
        cout << x << " ";
    cout << endl;

    for(auto x : b)
        cout << x << " ";
}

Then you can pass your other container, or your C-style array. The parameter is passed by reference , so the array length information is available.

NB. Consider using auto&& or auto const & in the loops, to avoid making copies of the elements in the container.

Upvotes: 0

Ben Voigt
Ben Voigt

Reputation: 283624

You need to make a range<T*> structure to wrap your array, and define begin() and end() functions for it.

It can be as simple as

template<typename Iterator>
struct range { Iterator begin_, end_; };

template<typename T>
T begin(const range<T>& ar) { return ar.begin_; }

template<typename T>
T end(const range<T>& ar) { return ar.end_; }

template<typename T, size_t N>
range<T*> make_array_range(T (&array)[N])
{
     using std::begin; using std::end;
     return { begin(array), end(array) };
}

func( make_array_range(a1), make_array_range(a2) );

You can then easily write your func(T[N], T[M]) overload using this building block.

You could also write a templated (on N) constructor if you don't like the factory function approach.

It can be used in place of any standard container because it supports the begin/end operations. And it can refer to a whole array, contiguous subset of an array, or to a contiguous subsequence of any standard container.

Upvotes: 2

Sam Varshavchik
Sam Varshavchik

Reputation: 118310

somehow calling f(const T&, const T&) without copying array elements

That, of course, won't work since that template function returns two parameters of the same type.

What you do need to do is to simply pass the arrays as pointers, and give the size of each corresponding array. After all, the very first thing one learns about arrays is that an array is convertible to a pointer to the first element of the array. so:

template<class T>
void func(const T *a, const T *b, size_t a_size, size_t b_size) {

   // ...
}

Now, your

template<class T, size_t M, size_t N>
void func(const T (&a)[M], const T (&b)[N])

should be able to simply do something like this:

func(&a[0], &b[0], M, N);

You "real" func() will then know the size of each array.

Upvotes: 0

Related Questions