14

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

3
  • 4
    If you must use a variable for the initialization, then I suggest you create a constructor for the Color structure. Commented Nov 5, 2018 at 14:38
  • @Someprogrammerdude OP says they can not add constructor, and there is a way to avoid the need for it. Commented Nov 5, 2018 at 16:00
  • This code is a very wrong approach to C++ ... it is almost C98. The obvious thing to do is Color c = { 10, 2, 6, 9} Commented Nov 5, 2018 at 22:58

4 Answers 4

18

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]}};
Sign up to request clarification or add additional context in comments.

11 Comments

I do not think it answers OP's question, looks like they need to initialize from a variable.
@SergeyA ¯\_(ツ)_/¯. The OP hasn't responded for me to know. I figured they used the array object because they didn't know they didn't need it. Hopefully they will respond at some point.
@SergeyA Because your answer is half a screen filled with template noise?
@SergeyA No, I'm saying (and the author of this answer) that sometimes manual repetition is perfectly fine, easier to understand and easier to maintain.
@pipe I believe a generic question is more important than the specific one - how do you initialize one C-style array from another. Future readers are less likely to benefit from the narrow answer than from the the wider scope one.
|
5

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.

5 Comments

No need to make make_c a template: length of Color::rgb is known at compile time, so you can just use it (e.g. sizeof Color::rgb/sizeof*Color::rgb or, since C++17, std::size(decltype(Color::rgb){})).
@Ruslan, yes, but than we wouldn't be able to present a nice message, and instead a substitute error would be displayed if sizes do not match
With std::apply and std::experimental::to_array the definiton of make_c can be shortened to auto make_c = [](double grey, auto&& rgb) { return std::apply([&](auto&&... args) { return Color{ grey, { args... } }; }, std::experimental::to_array(rgb)); but Color{ grey, { rgb[0], rgb[1], rgb[2] } } is still much simpler and easier to understand.
@cpplearner I already argued that a wider-scoped answer is better than narrow-scoped, and manual indexing of elements is not scalable. to_array is a good suggestion.
Both of you (SergeyA & NathanOliver ) have nice answers. As SergeA mentioned, a generic answers is most of the time expected. That was also my expectation when asking this question. However, when the generic answers become complicated it is nice to mention or illustrate how the problem can be solved in a simpler way as in the case of small array in NathanOliver answer. Thanks to both of you guys
4

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.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.