4
votes

I know how to write a variadic template function that accepts a variable number of arguments:

template<int I, typename... Rest>
void f() {
    // whatever
}

and I know how to write a template function that accepts a reference to an array:

template<typename T, unsigned int Length>
void f(T(&arr)[Length]) {
    // whatever
}

but I cannot think of how to combine the two so that the function accepts a variable number of references to arrays.

My first attempt was

template<typename T, unsigned int Length>
unsigned int arrlen(T(&)[Length]) {
    return Length;
}

template<typename T, unsigned int Length>
int f(T(&arr)[Length]) {
    return Length;
}

template<typename T, unsigned int Length, typename... Rest>
int f(T(&arr)[Length], Rest... rest) {
    return Length + f(rest...);
}

int main() {
    int a[] = {1 , 2, 3}, b[] = {1, 2, 3, 4, 5}, c[] = {1};

    cout << f(a, b, c);
}

But the compiler tells me

a.cpp: In function 'int f(T (&)[Length], Rest ...) [with T = int, unsigned int Length = 3u, Rest = {int*, int*}]'

a.cpp:23:22: instantiated from here

a.cpp:17:27: error: no matching function for call to 'f(int*&, int*&)'

a.cpp:17:27: note: candidates are:

a.cpp:11:22: note: template int f(T(&)[Length])

a.cpp:16:5: note: template int f(T(&)[Length], Rest ...)

So I was thinking that you could write an object that would store the length of the array it was constructed with, and then pass a variable number of those (which would be implicitly constructed from the arrays passed) to the function. Here is my try at that:

template<typename T, unsigned int Length>
struct Array {
    Array(T(&arr)[Length]) : arr(arr), len(Length) { }

    T(&arr)[Length];
    const unsigned int len;
};

int f() {
    return 0;
}

template<typename T, unsigned int Length, typename... Args>
int f(const Array<T, Length>& a1, Args... rest) {
    return a1.len + f(rest...);
}

int main() {
    int a[] = { 1, 2, 3 }, b[] = { 1, 2, 3, 4, 5 }, c[] = { 1 };

    cout << f(a, b, c);
}

But when I try to compile it with GCC 4.6.1 I get the error

a.cpp: In function 'int main()':

a.cpp:27:22: error: no matching function for call to 'f(int [3], int [5], int [1])'

a.cpp:27:22: note: candidates are:

a.cpp:16:47: note: template int f(const Array&, Args ...)

a.cpp:20:5: note: int f()

a.cpp:20:5: note: candidate expects 0 arguments, 3 provided

However, besides fixing the second code (which is more of a workaround for not knowing how to do what I really want to do), the actual point of this question and the thing I would actually like to learn is how to do this without using proxy objects like that if possible, like the first code. So how can this be done? Was there just a simple syntax error in one of the tries I posted?

2
Just use std::array and forget about raw array nonsense.Cat Plus Plus
@CatPlusPlus that would be the easy way out and wouldn't help me learn what's going on here.Seth Carnegie
@CatPlusPlus and actually that would present the same problemSeth Carnegie
No, it wouldn't. std::array doesn't decay to pointer, so you don't have to play with size passed as a template argument.Cat Plus Plus
@CatPlusPlus ah you're right, I forgot the problem is caused by the array-to-pointer decaySeth Carnegie

2 Answers

3
votes

If you just want to sum up the length of a number of arrays, you can do that directly:

template<typename T, unsigned int Length>
int f(const T (&)[Length]) {
    return Length;
}

template<typename T, unsigned int Length, typename... Args>
int f(const T (&)[Length], Args&... rest) {
    return Length + f(rest...);
}

int main() {
    int a[] = { 1, 2, 3 }, b[] = { 1, 2, 3, 4, 5 }, c[] = { 1 };

    std::cout << f(a, b, c);
}
2
votes

You can use std::extent to get the extent of the outer dimension of an array, and variadically sum those up:

#include <type_trait>

template <typename Arr, typename ...Rest> struct extent_sum
: std::integral_constant<std::size_t,
                         std::extent<T>::value + extent_sum<Rest...>::value> { };

template <typename T> struct extent_sum<T>
: std::integral_constant<std::size_t, std::extent<T>::value> { };

Usage:

const std::size_t n = extent_sum<int[2], char[4], float[3], bool>::value;