NonCreature0714 NonCreature0714 - 1 month ago 8
C++ Question

C++ what is this little used constructor syntax?

EDIT: I don't think this a duplicate of this other question, because the other question simply transposes

()
for
{}
in the constructors. Whereas I note different behavior when a constructor is defined in a
struct
, but not in a
class
. (And, as pointed out in the comments, this is about using constructors not writing them.) But I've been wrong before.

I came across this strange (to me) syntax for a constructor while tutoring:

Foo obj {i, j};


At first I thought it wouldn't work, and told the student to rewrite it – however they were adamant it worked, and informed me they pulled the example from cplusplus.com, to which I've not been able to find a reference, so I tried it anyway... And it worked. So I experimented with it.

I also researched a bit on my own, and found no reference to that kind of constructor syntax on cplusplus.com. (Maybe it has a specific name?)

Here's what I did to experiment with it.

struct Note { //A musical note.
std::string name;
double freq;
//Note(std::string s, double f): name(s), freq(f){}
//Uncomment the constructor in order to use normal constructor syntax.
};

class Journal {
public:
std::string title;
std::string message;
int idNum;
};

int main() {
Note a { "A", 440.0}; //Works with or without a constructor.
//Note a("A",440.0); //Works ***only*** with a defined constructor.

//Journal journal("hello, world", "just me, a class", 002); //Works regardless of constructor definition.
Journal journal {"hello, world", "just me, a class", 003}; //Works regardless of constructor definition.

std::cout << a.name << " " << a.freq << std::endl;
std::cout << journal.title << " " << journal.message << " " << journal.idNum << std::endl;

return 0;

}


I found that it works with structs and classes regardless if they have a defined constructor.

Obviously default constructors are at work, but this confused me because of a few reasons:


  • I've never seen this syntax before (not surprising, C++ is huge)

  • It actually works

  • Works regardless of a constructor being defined, so may be default behavior



My question is:


  • Is there a name and specific purpose for this syntax which sets it apart from regular constructor behavior, and if so, why use it (or not)?


Answer

Things have changed since the introduction of C++11 and you should probably read about list initialization

int x (0);  // Constructor initialization
int x {0};  // Uniform initialization

Before its introduction you had various initialization cases:

  • objects by calling the usual () constructor (and watch out for most vexing parse if no arguments are present)
  • aggregate classes or arrays with {}
  • default constructing with no braces

Now you can use uniform initialization in all of them.

It has to be noted that the two aren't really interchangeable since

std::vector<int> v (100); // 100 elements
std::vector<int> v {100}; // one element of value 100

and you have to pay attention to the fact that if the type has an initializer list constructor, it will take precedence in overload resolution.

That said, uniform initialization can be quite handy and safe (preventing narrowing conversions).