Инициализация частного члена std :: array в конструкторе - PullRequest
0 голосов
/ 07 июня 2018

Мне было интересно, как правильно инициализировать std::array член класса в конструкторе, когда начальные значения массива являются параметрами для конструктора?

Более конкретно, рассмотрим следующий пример:

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?
   {}
};

Очевидно, один способ - написать list({Car(color,age), Car(color,age), Car(color,age)}), но это явно не масштабируется, если мы хотели 30 одинаковых автомобилей вместо трех.

Если вместо std::array я использовал std::vector решение было бы list(3, Car(color,age) (или list(30, Car(color, age)), но, поскольку в моей проблеме известен размер списка, я подумал, что правильнее было бы использовать std:array.

Ответы [ 2 ]

0 голосов
/ 07 июня 2018

Отличный ответ от rmawatson.

Вот аналогичная альтернатива, которая пытается 2 улучшения:

  1. Построение по модели.
  2. Скопируйте модель N-1 раз ипереместите последний на место.

Конечно, для этого требуется, чтобы автомобиль можно было копировать.

#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);
}
0 голосов
/ 07 июня 2018

Один вариант для версии массива - использовать функцию шаблона для построения массива.Вам нужно будет проверить, оптимизировано ли это или скопировано в режиме выпуска,

   #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;
}

Демо

...