Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a particular syntax for initializing an std::array from another, different std::array?

I've this situation:

class A {
    ...
};

class B {
    public:
        B(A x) { .... }
}

std::array<A, some_constant_value> init;
std::array<B, some_constant_value> arr = {
    init[0], 
    init[1],
    init[2],
    ...... ,
    init[some_constant_value-1]
};

Is there, by any chance, a better syntax than this to avoid typing all the elements down? ( And that won't require meddling in the off-chance that some_constant_value will change? )

like image 927
Svalorzen Avatar asked Apr 28 '13 19:04

Svalorzen


2 Answers

I have this code lying around. I think it's what you want:

  template<unsigned... Indices>
  struct indices {
    using next = indices<Indices..., sizeof...(Indices)>;
  };

  template<unsigned N>
  struct build_indices {
    using type = typename build_indices<N-1>::type::next;
  };
  template<>
  struct build_indices<0> {
    using type = indices<>;
  };

  namespace impl {
    template<typename To, typename From, unsigned... Is>
    std::array<To, sizeof...(Is)>
    array_convert_impl(std::array<From, sizeof...(Is)> const& from, indices<Is...>) {
      return std::array<To, sizeof...(Is)>{{ from[Is]... }}; 
    }
  } // namespace impl
  template<typename To, typename From, unsigned N>
  std::array<To, N>
  array_convert(std::array<From, N> const& from) {
    return impl::array_convert_impl<To>(from, typename build_indices<N>::type());
  }

Then you can do:

std::array<B, some_constant_value> arr = array_convert<B>(init);
like image 102
Pubby Avatar answered Sep 29 '22 07:09

Pubby


An alternative solution provided by the Standard Library is:

std::array<B, some_constant_value> 
arr((std::copy(init.begin(),init.end(),(&arr)->begin()),arr));

Note that the argument of the constructor is enclosed by ((...)), so that it is correctly parsed as a comma-expression rather than as two arguments.

This solution relies upon the fact that B is implicitly constructible from A. A short solution that will also work if the converting constructor is made explicit is:

auto lamb = 
[&init]() -> B { static size_t i = 0; return B(init[i++]); };  
std::array<B, some_constant_value> 
arr((std::generate((&arr)->begin(),(&arr)->end(),lamb),arr));

The following test program, built with GCC 4.7.2, clang 3.2 and Intel C++ 13.1.1, (options -g -O0 -Wall -std=c++11) illustrates both solutions:

#include <iostream>
#include <array>
#include <algorithm>

struct A 
{
    int _i = 42;
};

struct B 
{
    B(A x) 
    : _i(x._i){}
    int _i; 
};

struct C 
{
    explicit C(A x) 
    : _i(x._i){}
    int _i; 
};

using namespace std;

int main()
{
    array<A, 10> init;
    array<B, 10> arr((copy(init.begin(),init.end(),(&arr)->begin()),arr));
    cout << "arr contains..." << endl;
    for (size_t i = 0; i < arr.size(); ++i) {
        cout << arr[i]._i << endl;
    }
    auto lamb = 
    [&init]() -> C { static size_t i = 0; return C(init[i++]); };  
    array<C, 10> brr((generate((&brr)->begin(),(&brr)->end(),lamb),brr));
    cout << "brr contains..." << endl;
    for (size_t i = 0; i < brr.size(); ++i) {
        cout << brr[i]._i << endl;
    }
    return 0;
}
like image 42
Mike Kinghan Avatar answered Sep 29 '22 07:09

Mike Kinghan