4

I am using the below code and getting error. I don't understand why I am getting this error.

prog.cpp: In function ‘int main()’:
prog.cpp:15:44: error: could not convert ‘{"foo", true}’ from 
                       ‘<brace-enclosed initializer list>’ to ‘option’
                       option x[] = {{"foo", true},{"bar", false}};
                                            ^
prog.cpp:15:44: error: could not convert ‘{"bar", false}’ from 
                       ‘<brace-enclosed initializer list>’ o ‘option’

The code

#include <iostream>
#include <string>
 
struct option
{
    option();
    ~option();
 
    std::string s;
    bool b;
};
 
option::option() = default;
option::~option() = default;

int main()
{
    option x[] = {{"foo", true},{"bar", false}};
}
JeJo
  • 30,635
  • 6
  • 49
  • 88
  • Just remove the default constructor and destructor or add a constructor that takes two arguments. – Ted Lyngmo Jun 29 '20 at 20:26
  • 1
    What version of C++ are you compiling against? The rules for aggregates have changed in every version so it matters. – NathanOliver Jun 29 '20 at 20:26
  • IMO, I think that the braces reduces encapsulation for the `option` class if the two argument constructor isn't provided. Why must the user know that there are two member variables that can be set like that? I could understand something like `vector`, where you know that you use `int`'s to populate the object, but `option`? Provide the proper constructors that makes it evident that an `option` consists of two values, and the constructor of `object` figures out how to set those values. – PaulMcKenzie Jun 29 '20 at 20:37

1 Answers1

6

When you provide the default constructor and destructor, you are making the struct be a non-aggregate type, hence aggregate initialization is not possible.

However, you can check if a type is an aggregate using the standard std::is_aggregate_v trait. (Since ).

See here for your case. It is not an aggregate, as you provided those constructors.

You have the following three ways to make this work:


The following post explains when the constructor will be defaulted, when it is considered as user-declared and user-provided clearly: (Credits @NathanOliver)

C++ zero initialization - Why is `b` in this program uninitialized, but `a` is initialized?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
JeJo
  • 30,635
  • 6
  • 49
  • 88
  • is there any other way without removing the default constructor. – Ashutosh Kumar Ray Jun 29 '20 at 20:29
  • 1
    @AshutoshKumarRay You can add a constructor that takes two arguments. – Ted Lyngmo Jun 29 '20 at 20:30
  • It's a little more subtle then your answer makes it seem. For instance, this compiles fine: http://coliru.stacked-crooked.com/a/856bbadf418fe86a – NathanOliver Jun 29 '20 at 20:30
  • 2
    @JeJo but this one is, and it has the members explicitly defaulted: https://godbolt.org/z/o6q-Z7 – NathanOliver Jun 29 '20 at 20:33
  • 5
    When declaring a constructor inside the class as default, it is considered a user-declared constructor. When defaulting it outside the class, it is a user-provided constructor. Depending on the version of C++, that will make a difference if it is an aggregate or not. See: https://stackoverflow.com/questions/54350114/c-zero-initialization-why-is-b-in-this-program-uninitialized-but-a-is-i/54350350#54350350 – NathanOliver Jun 29 '20 at 20:45