1

I've got Foo.h:

#include <array>

class Bar {
public:
  Bar(std::string name) : name(name) {}

  std::string name;

};

class Foo {
public:
  enum {ARRAY_SIZE=10};

  Foo();

  void printElement(int idx);

  std::array<Bar,ARRAY_SIZE> myArray;

};

Foo.cc:

#include "Foo.h"
#include <iostream>

Foo::Foo(): myArray({Bar("9"),Bar("8"),Bar("7"),Bar("6"),Bar("5"),
                     Bar("4"),Bar("3"),Bar("2"),Bar("1"),Bar("0")}) {}


void Foo::printElement(int idx) {
  if (idx < ARRAY_SIZE) {
    std::cout << "Value is " << myArray[idx].name << std::endl;
  } else {
    std::cout << "Index out of bounds" << std::endl;
  }
}

int main() {
  Foo foo;
  foo.printElement(1);
}

The problem is that the {Bar("9"),Bar("8"),Bar("7"),Bar("6"),Bar("5"),Bar("4"),Bar("3"),Bar("2"),Bar("1"),Bar("0")} is too literal, I need to be able to use an expression which expands to the right size based on the value of ARRAY_SIZE (which could live in an external header file).

Note that in this example I must initialize myArray in the initializer list, otherwise I get this:

(...)/gcc/6.3.0/include/c++/6.3.0/array:90:12: error: no matching function for call to 'Bar::Bar()'

What's the best way to do it?

I'm using g++ 6.3.0 by the way.

3
  • myarray has the "right size" (i.e. ARRAY_SIZE) when you declare it. That is, you don't need to initialize a std::array to give it a size. The size is part of the array type. Commented Dec 12, 2019 at 16:24
  • Is the ARRAY_SIZE always fixed? Why not have it as a template parameter? Commented Dec 12, 2019 at 16:38
  • @Jasancos: This site has an edit history; when you update your question you don't need to keep old versions. That just confuses new readers. Commented Dec 12, 2019 at 16:55

1 Answer 1

4

Use a helper function. Add

template <std::size_t N>
auto make_array()
{
    std::array<Bar, N> arr;
    for (std::size_t i = 0; i < N; ++i)
        arr[i] = std::to_string(N - i - 1);
    return arr;
}

To your class and then you can use it in the member initializer list like

Foo::Foo(): myArray(make_array<10>()) {}

This only works through if Bar is default constructable.

For your code what you can do is add another helper that has a std::index_sequence of values to construct the Bar's from and that would look like

// helpers for reversing an integer sequence
template <std::size_t ... Is>
constexpr auto indexSequenceReverse (std::index_sequence<Is...> const &)
   -> decltype( std::index_sequence<sizeof...(Is)-1U-Is...>{} );

template <std::size_t N>
using makeIndexSequenceReverse
   = decltype(indexSequenceReverse(std::make_index_sequence<N>{}));

class Bar {
public:
  Bar(std::string name) : name(name) {}

  std::string name;

};

class Foo {
public:
  enum {ARRAY_SIZE=10};

  Foo();

  void printElement(int idx);

  std::array<Bar,ARRAY_SIZE> myArray;

  // get sequence and expand out initializes 
  template <std::size_t ... Is>
  auto make_array_impl(std::integer_sequence<size_t, Is...>)
  {
      return std::array{ Bar{std::to_string(Is)}... };
  }

  // convenient forwarder to implementation
  template <std::size_t N>
  auto make_array()
  {
      return make_array_impl(makeIndexSequenceReverse<N>{});
  }

};

Foo::Foo(): myArray(make_array<10>()) {}

void Foo::printElement(int idx) {
  if (idx < ARRAY_SIZE) {
    std::cout << "Value is " << myArray[idx].name << std::endl;
  } else {
    std::cout << "Index out of bounds" << std::endl;
  }
}

int main() {
  Foo foo;
  foo.printElement(1);
}
Sign up to request clarification or add additional context in comments.

11 Comments

Many thanks for the solution, but my original example didn't describe my problem. I've edited the question accordingly
@Jasancos No problem. I've updated the answer to work for Bar's
If you want Bar("nine"), Bar("eight"), etc you'll need a second helper function instead of std::to_string but this indeed is the right solution.
@Jasancos There isn't one. That function works like std::declval works. It exists solely to get a type that can be used. It's a template meta programming technique.
@Jasancos If you can use C++17 the code will compile as is thanks to CTAD (Class Template Argument Decuction). If you can't use C++17 then you need return std::array<Bar, sizeof...(Is)>{ Bar{std::to_string(Is)}... };
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.