Gaetan
Gaetan

Reputation: 607

Initialize object containing C-style array as member variable (C++)

Consider the following code:

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

How can I initialize a Color object in one line?

In my real case scenario, Color struct can't be change (for example, to use std::array instead of a C-style array).

Upvotes: 14

Views: 2627

Answers (4)

Jans
Jans

Reputation: 11250

As complement to others answers, the error is because in arrays are not copyable and trying to initialize an array from an lvalue has the semantic of invoking the copy-constructor, the same as:

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

Your options are:

  • do list-initialization as @NathanOliver did
  • or expand the elements of the array to form a list-initialization as in the @SergeyA answer.

Upvotes: 4

user10133158
user10133158

Reputation:

Call me a cheat, but...

 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] ) ;
}

But, since that is pretty atrocious C++, I have taken a liberty of producing something slightly better...

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);    

}

All in a good old SO tradition: do not question the question :)

(the mandatory Wandbox is here)

ps: I like your approach Oliver, although you do not need double braces in those init lists, of course.

Upvotes: 1

SergeyA
SergeyA

Reputation: 62553

Supposing that there is a need to use an intermediate array, here is how one can do it:

#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

Note, that this code will not actually produce any unnecessary copying with any level of optimization.

Upvotes: 5

NathanOliver
NathanOliver

Reputation: 180415

Since Color is an aggregate you can use aggregate initialization and put the array initializer directly in the braces like

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

If you have to initialize c with an array, since it is small, you can just unroll it like

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

Upvotes: 18

Related Questions