I was wondering what is the proper way to initialize a std::array
member of the class in the constructor, when the initial array values are parameters to the constructor?
More specifically, consider the following example:
class Car {
public:
Car(const std::string& color, int age): color_(color), age_(age) {}
// ...
private:
std::string color_;
int age_;
};
class ThreeIdenticalCars {
private:
std::array<Car, 3> list;
public:
ThreeIdenticalCars(const std::string& color, int age):
// What to put here to initialize list to 3 identical Car(color,age) objects?
{}
};
Obviously one way is to write list({Car(color,age), Car(color,age), Car(color,age)})
, but this clearly does not scale if we wanted 30 identical cars instead of three.
If instead of std::array
I used std::vector
the solution would have been list(3, Car(color,age)
(or list(30, Car(color, age))
but as in my problem the size of the list is known, I thought it is more correct to use std:array
.
One option for the array version is to use a template function to build the array. You'll have to test to see if this gets optimized out or copied in release mode,
#include <iostream>
#include <array>
#include <tuple>
class Car {
public:
Car(const std::string& color, int age): color_(color), age_(age) {}
// ...
//private:
std::string color_;
int age_;
};
template <typename CarType, typename... Args ,size_t... Is>
std::array<CarType,sizeof...(Is)> make_cars(std::index_sequence<Is...>,Args&&... args )
{
return { (Is,CarType(args...))... };
}
class ThreeIdenticalCars {
//private:
public:
std::array<Car, 3> list;
//public:
ThreeIdenticalCars(const std::string& color, int age) :
list(make_cars<decltype(list)::value_type>(
std::make_index_sequence<std::tuple_size<decltype(list)>::value>(),
color,
age
))
{}
};
int main()
{
ThreeIdenticalCars threecars("red", 10);
for(auto& car : threecars.list)
std::cout << car.color_ << " " << car.age_ << std::endl;
return 0;
}
Great answer by rmawatson.
Here's a similar alternative which attempts 2 enhancements:
- Construction by model.
- copy the model N-1 times and move the last one into place.
Of course it requires that a Car is copy-constructible.
#include <array>
#include <string>
class Car {
public:
Car(const std::string& color, int age): color_(color), age_(age) {}
// ...
private:
std::string color_;
int age_;
};
namespace detail
{
template<std::size_t...Is, class Model>
auto build_array_impl(std::index_sequence<Is...>, Model&& model)
{
constexpr auto size = sizeof...(Is) + 1;
return std::array<std::decay_t<Model>, size>
{
// N-1 copies
(Is, model)...,
// followed by perfect forwarding for the last one
std::forward<Model>(model)
};
}
}
template<std::size_t N, class Type>
auto build_array(std::integral_constant<std::size_t, N>, Type&& model)
{
return detail::build_array_impl(std::make_index_sequence<N-1>(),
std::forward<Type>(model));
}
class ThreeIdenticalCars {
private:
static constexpr auto num_cars = std::size_t(3);
static constexpr auto num_cars_c = std::integral_constant<std::size_t, num_cars>();
std::array<Car, num_cars> list;
public:
ThreeIdenticalCars(const std::string& color, int age)
: list(build_array(num_cars_c, Car(color, age)))
{}
};
int main()
{
ThreeIdenticalCars tic("red", 1);
}
来源:https://stackoverflow.com/questions/50741720/initializing-private-stdarray-member-in-the-constructor