return a reference to an array with templated size

Viewed 58

I am trying a function's return value to be to a reference to an array whose size is variable (i.e. templated). The code I have is:

const char *strarr[] = { "one", "two", "three" };

template <std::size_t T>
const char* (&GetArr())[T]
{
    return strarr;
}

The reason I want to do it this way: it would be convenient in my case to then use in a a loop such that:

for (const auto& s : GetArr()) { std::cout << s << std::endl; } 

but this wouldn't compile. the only way to use it is to specify the size in GetArr(); but I was wondering why it can't deduce the size by itself, as it would have if I passed a reference to this array as a parameter to a templated function, as in:

template <std::size_t T>
void PrintArray(const char* (&arr)[T])
{
    for (const auto& s : arr)
    {
        std::cout << s << std::endl;
    }
}

which does work.

1 Answers

There is no template argument deduction from return statements and T cannot be deduced from any function parameter/argument pair.

So you will have to specify the size manually, but that is not a problem, since you need to specify the array that is returned anyway, so that the only possible choice is

const char* (&GetArr())[std::size(strarr)]
{
    return strarr;
}

That said, there is return type deduction via type placeholders which you can make use of here easily:

auto& GetArr()
{
    return strarr;
}

Note that in both cases the function is not a function template. There aren't multiple different instantiations that would make sense for it. There is only one possible size that makes sense, one possible return type and one possible function body. Therefore template <std::size_t T> doesn't really make sense to begin with.

However, it is much simpler to just use std::array instead of built-in arrays. The former doesn't require you to take care of special language rules or weird declaration syntax as in the first example.


There is also no need to write PrintArray so restrictively. You can instead write

template <typename R>
void PrintRange(const R& r)
{
    for (const auto& s : r)
    {
        std::cout << s << std::endl;
    }
}

and now you have a function that will work with any range, whether built-in array, std::array, std::vector, std::list, etc.

Related