4

Coming from C to C++, I'm trying to understand the world of smart pointers & references. I've got the following:

class Game {
public:
    ...
private:
    ...
    static GamePiece EmptyPiece;
    reference_wrapper<GamePiece> _board[N][M] = { ref(Game::EmptyPiece) };
    vector<GamePlayer> _players = vector<GamePlayer>(N_PLAYERS, GamePlayer());
    ...
};

In the following situation, I would like each Player to hold a vector<GamePiece> and return references to these pieces, and put then in the _board. However, the following initialization of my _board yields

no default constructor exists for class "std::reference_wrapper

What am I missing here? In terms of ownership, each GamePlayer is owned by the Game (as can be seen), and the GamePieces are definitely owned by the GamePlayers, and that's why I want to use references.

galah92
  • 3,621
  • 2
  • 29
  • 55

2 Answers2

2

It's this here

reference_wrapper<GamePiece> _board[N][M] = { ref(Game::EmptyPiece) };

You initialize the first element (with some brace elision thrown in) but leave the rest default initialized. Which can't happen, since std::reference_wrapper cannot be default initialized (just like the reference it models).

You can substitute the raw array for a std::vector of N*M size, and use the appropriate constructor which will copy initialize all the elements (like you do for _players). Of course, you'll need to do the calculations for indexing by yourself, but the memory will be laid out sequentially.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • I though it initialized the entire array - thanks. Is there a better container than a `vector`, where the notion of the dimensions is still clear? – galah92 Apr 15 '18 at 05:20
  • 1
    @galah92 - You can use a vector of vectors in a pinch. But I think a flat memory layout is better. The logical dimensions of the board don't mean it should have physical dimensions. But if you want to easily access via logical dimensions, you can wrap the vector in a [small type that offers that appropriate `operator()`](https://isocpp.org/wiki/faq/operator-overloading#matrix-subscript-op). – StoryTeller - Unslander Monica Apr 15 '18 at 05:33
  • @galah92 you can use a `std::array` of `std::array` if you know the size at compile time – Guillaume Racicot Apr 15 '18 at 05:45
  • 2
    @GuillaumeRacicot - That doesn't escape the problem of needing to initialize all the elements to prevent their default initialization. It's just cleaner than a 2d raw array. – StoryTeller - Unslander Monica Apr 15 '18 at 05:46
  • @StoryTeller ah yes, true. – Guillaume Racicot Apr 15 '18 at 06:02
1

Initializing an array of references is pain in my opinion. The problem is -- as said in the answer by @StoryTeller -- that a reference_wrapper is not default constructible.

So you have to write your own workaround functions. I'll post code for the general problem of initializing an array of references and won't dive deeply into your question.

So consider the following case: you have an array arr holding elements of some type (e.g. a Game as in your question) that supports operator[]. You want an array of const- or non-const references to elements in this array specified by the indices ind. Here you go:

template<typename arr_t, size_t ... I>
auto get_const_reference_array(arr_t const& arr, std::array<size_t, sizeof ...(I)> const& ind, std::index_sequence<I...>)
{
    using T = std::decay_t<decltype(std::declval<arr_t>().operator[](size_t{}))>;
    return std::array<std::reference_wrapper<const T>, sizeof ...(I)> { std::cref(arr[std::get<I>(ind)]) ... };
}
template<typename arr_t, size_t dim>
auto get_const_reference_array(arr_t const& arr, std::array<size_t, dim> const& ind)
{
    return get_const_reference_array(arr, ind, std::make_index_sequence<dim>{});
}

For the non-const version, remove all const's in this code and replace std::cref by std::ref.

Use it as

std::array<int,5> arr{{1,3,5,7,9}};
std::array<size_t,2> ind{{1,3}};
auto ref_arr = get_const_reference_array(arr, ind);

std::vector<int> vec{{1,3,5,7,9}};
auto ref_vec = get_const_reference_array(vec, ind);

ref_arr then is an array of size 2 which holds const references to arr[1] and arr[3], and the same for the vector (note however that references to a vector are in general not stable, i.e. by resizing or similar actions they might get invalidated).

davidhigh
  • 14,652
  • 2
  • 44
  • 75