31
struct SS {int a; int s;};

int main ()
{
   vector<SS> v;
   v.push_back(SS{1, 2});
}

The code can be compiled without any error. However, when the struct is initialized in class, I got compilation error. Can anyone explain it?

struct SS {int a = 0; int s = 2;};

Error:

In function ‘int main()’:
error: no matching function for call to ‘SS::SS(<brace-enclosed initializer list>)’
     v.push_back(SS{1, 2});
                        ^
note: candidates are:
note: constexpr SS::SS()
 struct SS {int a = 0; int s = 2;};
        ^
note:   candidate expects 0 arguments, 2 provided
note: constexpr SS::SS(const SS&)
note:   candidate expects 1 argument, 2 provided
note: constexpr SS::SS(SS&&)
note:   candidate expects 1 argument, 2 provided
Michael D
  • 1,449
  • 5
  • 18
  • 31

3 Answers3

44

In C++11, when you use non static data member initialization at the point of declaration like you do here:

struct SS {int a = 0; int s = 2;};

you make the class a non-aggregate. This means you can no longer initialize an instance like this:

SS s{1,2};

To make this initialization syntax work for a non-aggregate, you would have to add a two-parameter constructor:

struct SS 
{
  SS(int a, int s) : a(a), s(s) {}
  int a = 0; 
  int s = 2;
};

This restriction has been lifted in C++14.

Note that you may want to add a default constructor for the class. The presence of a user-provided constructor inhibits the compiler generated default one.

See related reading here.

Community
  • 1
  • 1
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • 3
    Right, I was about to answer similarly. It can be seen without C++11 features too, by adding a custom constructor `SS() {}`. –  Aug 12 '13 at 10:02
  • 4
    C++14 will no longer exclude classes with in-class initializers from being aggregates; See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3653.html – bames53 Aug 12 '13 at 14:48
  • 1
    @bames53 Thanks, I did not know that. It is great news. I find this particular rule over-restrictive. – juanchopanza Aug 12 '13 at 15:12
5

Use of a default member initializer renders the class/struct a non-aggregate:

§ 8.5.1 Aggregates

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

Semantics differ for aggregates and non-aggregates:

Aggregates (e.g., arrays and structs):

Initialize members/elements beginning-to-end.

Non-aggregates:

Invoke a constructor.

v.push_back(SS{1, 2}); // Error, it tries to call SS constructor

Which means you need a constructor now:

struct SS 
{
  SS(int a, int s) : a(a), s(s) 
  {
  }
  int a = 0; 
  int s = 2;
};
billz
  • 44,644
  • 9
  • 83
  • 100
  • Interestingly, this is no longer true in C++1y, aggregate members are allowed to have brace-or-equal-intializers. N3690 §8.5.1/7 says of initializing aggregates from braced-init-lists: "If there are fewer *initializer-clauses* in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from its *brace-or-equal-initializer* or, if there is no *brace-or-equal-initializer*, from an empty initializer list (8.5.4)." – Casey Aug 12 '13 at 14:08
1

I had the same problem. In my case, I had two structs that both had a few constructors, including copy constructors, inheriting from an abstract parent.

When the advice above didn't help, I finally realized I needed to remove explicit specifier from the copy constructors and that removed the error.

Thought I would share in case another poor soul spends as long finding this mistake as I just did.

Jackson H
  • 171
  • 10