2

Let's say I have c_student class with several members and no constructors:

#include <iostream> #include <string> #include <initializer_list> #include <typeinfo> class c_student{ // private: public: std::string name = "John"; int mark = 5; std::string second_name="Doe"; void print(){ std::cout << name <<" " << second_name << " : "<< mark<<"\n"; } }; int main(){ c_student stud_c = {"Luke ",420}; stud_c.print(); c_student stud_d = {"Mark "}; stud_d.print(); } 

This code works fine, but let's say I want to define custom constructors inside the class:

 c_student (std::string n):name(n){}; c_student() = default; 

If I add them to class, the compiler complains that:

47_object_initialization.cpp:32:34: error: could not convert ‘{"Luke ", 420}’ from ‘<brace-enclosed initializer list>’ to ‘c_student’ 32 | c_student stud_c = {"Luke ",420}; | ^ | | | <brace-enclosed initializer list> 

I want to keep using default constructor for {} and so need to write something like:

c_student( std::initializer_list <???>) = default; << Pseudo-code only! 

But can't get how exactly. Could anyone points my attention on the right page of CPP reference?

3 Answers 3

2

This is unfortunately not possible. The reason your code originally worked is due to Aggregate initialization. As soon as you created a user-defined constructor, your class is no longer an aggregate type

An aggregate is one of the following types:

  • class type that has
    -- no user-declared constructors
Sign up to request clarification or add additional context in comments.

3 Comments

Are you sure? Maybe it is only possible in later standards, but there is some related info here: stackoverflow.com/questions/21941967/…
@SvenNilsson The reason that code works is they provided a user-defined constructor for B, which the OP is free to do here as well. However the OP here wants to "bring back" the default aggregate initialization behavior, which as the standard mentions is not possible. They would have to define a user-defined constructor to emulate that kind of behavior, which undermines the default compiler generated behavior.
Ok, I understand. You are right but there is a workaround...
2

You can use brace-initialization to achieve what you want by replacing all the constructors with the following one:

c_student (std::string name = "John", int mark = 5, std::string second_name = "Doe") : name(name), mark(mark), second_name(second_name) {}; 

Also, you can remove the default member initializers since the only way to construct a c_student will give them the default values if they are not provided.

Here's a demo.

4 Comments

Nice and efficient. However, what if the order of list elements is different, like {"First", "Last", 20} ?
@AdrianMole Without C++20 designated initializers, there's no way to muck around with the order of parameters.
You can provide explicit (and separate) constructors with different argument orders.
@AdrianMole Sometimes, but I think all arguments would need to be different types. e.g. how would you disambiguate between c_student("Adrian")? Is that the first, or second name? Hmm, actually UDL would work be a fix for that, but it's a lot of work just for different constructor orders.
1

You can add another user-defined constructor to handle the initializer list with two entries (and keep adding such constructors for each possible list size and order). The following compiles and works as expected:

#include <iostream> #include <string> class c_student { private: std::string name = "John"; int mark = 5; std::string second_name = "Doe"; public: void print() { std::cout << name << " " << second_name << " : " << mark << "\n"; } c_student(std::string n) : name(n) { } // Called for initializer list {"xxx"} c_student(std::string n, int m) : name(n), mark(m) { } // Called for list {"xxx", m} c_student(std::string n, std::string s, int m) : name(n), mark(m), second_name(s) { } // Called for list {"xxx", "yyy", m} c_student() = default; }; int main() { c_student stud_c = { "Luke", 420 }; stud_c.print(); c_student stud_d = { "Mark" }; stud_d.print(); c_student stud_e = { "John", "Smith", 333 }; stud_e.print(); return 0; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.