4

suppose we have the following class in C++11 or later:

class MyClass {
private:
    const std::array<SomeType, 100> myArray;
public:
    explicit MyClass(std::array<SomeOtherType, 100> initArray);
};

Assuming that class SomeType has a constructor that takes a single SomeOtherType as an argument, is it possible to initialize the const member array using list-initialization in the constructor? What is the syntax for doing so?

Clearly, just directly initializing it like this doesn't work:

MyClass::MyClass(std::array<SomeOtherType, 100> initArray) :
    myArray{initArray} {}

Thanks!

Embedder
  • 43
  • 1
  • 4
  • 2
    You are aware that your class with a const member won't be copyable? – Swordfish Nov 27 '18 at 03:55
  • if you are using c++11 (or newer), look up documentation for `std::initializer_list`. If you are compiling with GCC, you may also find this post useful https://stackoverflow.com/questions/8192185/using-stdarray-with-initialization-lists – HappyKeyboard Nov 27 '18 at 03:58
  • @Swordfish: Thanks for the heads up - that shouldn't be a problem for my current use case, but I'll keep that in mind for the future. – Embedder Nov 27 '18 at 04:37
  • @HappyKeyboard: Thanks, I'll take a look at std::initializer_list. Also good to know about the need for double curly brackets with std::array and gcc! – Embedder Nov 27 '18 at 04:39

3 Answers3

4

You can use a variadic template:

#include <array>

struct foo
{
    const std::array<int, 10> bar;

    template<typename... T>
    foo(T&&... t)
    : bar({ std::move(t)... })
    {}
};

int main()
{
    foo f{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
}

Or you can initialize it with an array passed to the constructor:

#include <array>

struct foo
{
    const std::array<int, 10> bar;

    explicit foo(std::array<int, 10> const &qux)
    : bar{ qux }
    {}
};

int main()
{
    std::array<int, 10> qux;
    foo f(qux);
}

But these options don't take into account that you want to have an array of SomeOtherType converted to an array of SomeType. I didn't realize that at first, hece the variants above.

#include <cstddef>
#include <array>
#include <utility>

struct SomeOtherType{};

struct SomeType {
    SomeType(SomeOtherType) {}
};

struct MyClass
{
    const std::array<SomeType, 100> myArray;

    template<typename T, std::size_t... N>
    MyClass(T&& qux, std::index_sequence<N...>)
    : myArray{ qux[N]... }
    {}

    explicit MyClass(std::array<SomeOtherType, 100> const &qux)
    : MyClass{ qux, std::make_index_sequence<100>{} }
    {}
};

int main()
{
    std::array<SomeOtherType, 100> qux{};
    MyClass foo(qux);
}
Swordfish
  • 12,971
  • 3
  • 21
  • 43
1

You can unpack arguments with std::index_sequence and delegating constructors

template<typename Arr, size_t... Is>
MyClass(Arr&& arr, std::index_sequence<Is...>)
  : myArray{arr[Is]...} ()

explicit MyClass(std::array<SomeOtherType, 100> arr) : MyClass(arr, std::make_index_sequence<100>{}) ()
Passer By
  • 19,325
  • 6
  • 49
  • 96
0

This is possible. You just need a little helper function template to do the conversion for you. Something like this:

template <class T, class U, size_t N>
std::array<T, N> ArrayConvert(std::array<U, N> const& init)
{
  std::array<T, N> result;
  std::copy(init.begin(), init.end(), result.begin());
  return result;
}

class Foo
{
  std::array<int, 100> myArray;
public:
  template <class U> Foo(std::array<U, 100> const& init)
    : myArray(ArrayConvert<int>(init))
  {
  }
};
Peter Ruderman
  • 12,241
  • 1
  • 36
  • 58