Reputation: 1725
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
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
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
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