Инициализировать объект, содержащий массив в стиле C, как переменную-член (C++)

Рассмотрим следующий код:

struct Color  // This struct can't be modified
{
    double grey;
    double rgb[3];
};

int main()
{
    double myRGB[3] = {2, 6, 9};

    Color c = {10, myRGB}; // This line doesn't work

    return 0;
}

Как я могу инициализировать объект Color в одну строку?

В моем реальном сценарии структуру Color нельзя изменить (например, использовать std::array вместо std::array в стиле C).

Ответ 1

Предположим, что нужно использовать промежуточный массив, вот как это можно сделать:

#include <utility>
#include <cstddef>

struct Color  //this struct can't be modified
{
    double grey;
    double rgb[3];
};

template<size_t N, size_t... IX>
auto make_c_impl(double grey, double (&rgb)[N], std::index_sequence<IX...>) {
    static_assert(sizeof(rgb) == sizeof(Color::rgb), "Arrays sizes must match!");
    return Color{grey, {rgb[IX]...}};
}

template<size_t N>
auto make_c(double grey, double (&rgb)[N]) {
    return make_c_impl(grey, rgb, std::make_index_sequence<N>{});
}
double myRGB[3] = {2, 6, 9};

Color c = make_c(10, myRGB); //this line now works

Обратите внимание, что этот код на самом деле не приведет к ненужному копированию с любым уровнем оптимизации.

Ответ 2

Поскольку Color является агрегатом, вы можете использовать агрегатную инициализацию и поместить инициализатор массива непосредственно в фигурные скобки, например

Color c = {10, {2, 6, 9}};

Если вам нужно инициализировать c с массивом, так как он маленький, вы можете просто развернуть его как

Color c = {10, {myRGB[0], myRGB[1], myRGB[2]}};

Ответ 3

Как дополнение к другим ответам, ошибка заключается в том, что в массивы не копируются, и попытка инициализировать массив из lvalue имеет семантику вызова copy-constructor, такую же как:

double r1[3] = {0., 0., 0.};
double r2[3] {r1} // doesn't compile

Ваши варианты:

  • сделать list-initialization как @NathanOliver
  • или разверните элементы массива, чтобы сформировать list-initialization как в ответе @SergeyA.

Ответ 4

Назови меня читом, но...

 struct Color final  
{
    double grey;
    double rgb[3];
};
// the cheet
#define make_color( x, a, b ) Color x { a, b[0], b[1], b[2] }

int main()
{
    double myRGB[3]{ 2, 6, 9 };

    make_color( c, 10, myRGB ) ; // single line construction 

    printf("\nColor grey: %f\t rgb:[ %f, %f, %f ]", c.grey, c.rgb[0], c.rgb[1], c.rgb[2] ) ;
}

Но, поскольку это довольно жестоко C++, я позволил себе создать что-то немного лучше...

struct Color final  
{
    double grey;
    double rgb[3];
};

auto  make_color ( double a, const double(&b)[3] ) { return Color { a, b[0], b[1], b[2] }; }; 
auto  make_color ( double a, double b, double c, double d ) { return Color { a, b, c, d }; }; 
auto print_color ( Color c ) { printf("\nColor grey: %f\t rgb:[ %f, %f, %f ]", c.grey, c.rgb[0], c.rgb[1], c.rgb[2] ) ; }
//
int main()
{
    double myRGB[3]{ 2, 6, 9 };

    auto c = make_color( 10, myRGB ) ; 
    print_color(c);    
    auto z = make_color( 10, 0xFF, 0xA0, 0xB0 ) ; 
    print_color(z);    

}

Все по старой доброй традиции: не задавайте вопросов :)

(обязательный Wandbox здесь)

PS: Мне нравится ваш подход, Оливер, хотя вам, конечно, не нужны двойные скобки в этих списках инициализации.