Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 it's 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 do 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...

like image 642
ledonter Avatar asked Sep 14 '17 15:09

ledonter


3 Answers

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]>{});
}
like image 97
Vittorio Romeo Avatar answered Oct 16 '22 03:10

Vittorio Romeo


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;
}
like image 31
Barry Avatar answered Oct 16 '22 03:10

Barry


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 */
}
like image 30
dfrib Avatar answered Oct 16 '22 05:10

dfrib