ledonter
ledonter

Reputation: 1699

Returning reference to an array of specific size without explicitly stating the size in return type

I've got the following function:

... getX()
{
    static int x[] = {1, 2, 3};
    return x;
}

I'd like to have its return type as int(&)[3] but don't wan't to specify the size (3) explicitly.

How do I do that?

(Please don't ask why I want that.)

UPD

Well, OK, I need to pass a result to a template function taking int(&x)[N] as a parameter (and I don't want to pass the size explicitly to that template function), so I don't see how a solution with returning a pair could work...

Upvotes: 16

Views: 1620

Answers (4)

dfrib
dfrib

Reputation: 73186

C++11

Another C++11 alternative (workaround), in case your theoretical scenario (not asking why ...) allows wrapping the static array as a (literal) static data member of an otherwise stateless type:

class Foo
{
    static constexpr int x[] = {1, 2, 3};
    // delete ctor(s) ...
public:
    static auto getX() -> std::add_lvalue_reference<decltype(x)>::type { return x; }
};
constexpr int Foo::x[];

Or, e.g.

class Foo
{
    template <typename T, std::size_t n>
    static constexpr std::size_t array_size(const T (&)[n]) { return n; }

    static constexpr int x[] = {1, 2, 3};

    // delete ctor(s) ...
public:
    template<std::size_t N = array_size(x)>
    static const int (&getX())[N] { return x; }
};
constexpr int Foo::x[];

Any of the two above applied in the use case you describe in your question:

template <std::size_t N>
void feedX(const int (&x)[N])
{
    for (const auto num: x) { std::cout << num << "\n"; }    
} 

int main()
{
    feedX(Foo::getX()); /* 1
                           2
                           3 */
}

This wouldn't help you in case your theoretical scenario would need to mutate the static data, though. You could tweak the above into a mutating-allowing scenario, but at the cost of having to specify the size of x at its declaration, as it can no longer be (constant-)initialized and size-deduced at that point, and I believe this size explicitness is what you wanted to avoid in the first place. Anyway, for completeness:

class Foo
{
    static int x[3];
public:
    static auto getX() -> std::add_lvalue_reference<decltype(x)>::type { return x; }
};
int Foo::x[] = {1, 2, 3};

template <std::size_t N>
void feedAndMutateX(int (&x)[N])
{
    for (auto& num: x) { std::cout << num++ << "\n"; }    
} 

int main()
{
    feedAndMutateX(Foo::getX()); /* 1
                                    2
                                    3 */
    feedAndMutateX(Foo::getX()); /* 2
                                    3
                                    4 */
}

Upvotes: 2

If you really want a reference, and have C++14, then decltype(auto) with a parenthesized id expression:

decltype(auto) get_arr() {
    static int x[] = {1, 2 ,3};

    return (x);
}

Will deduce as a reference to the array of that size. See it live, where the type of the reference is shown in the error message.

Upvotes: 0

Barry
Barry

Reputation: 302942

Do you need the size available as a compile-time constant? I would suggest using gsl::span (or roll your own). This is basically just a pointer and a size, that satisfies the range concept:

gsl::span<int> getX()
{
    static int x[] = {1, 2, 3};
    return x;
}

Upvotes: 3

Vittorio Romeo
Vittorio Romeo

Reputation: 93274

In C++14:

auto& getX()
{
    static int x[] = {1, 2, 3};
    return x;
}

Also, consider using std::array instead of C-style arrays.


I cannot currently think of any Standard-compliant C++11 solution. Here's one using compound literals, assuming that your goal is to not repeat the elements and to deduce a reference-to-array:

#include <type_traits>

#define ITEMS 1, 2, 3
auto getX() -> decltype((int[]){ITEMS})
{
    static int x[] = {ITEMS};
    return x;
}
#undef ITEMS

int main()
{
    static_assert(std::is_same<decltype(getX()), int(&)[3]>{});
}

Upvotes: 19

Related Questions