Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting constructor with multiple arguments

In C++11, a constructor without explicit keyword can be used to convert a list of arguments implicitly to its class. For example:

class Date{
private:
  int d, m, y;
public:
  Date(int _d, int _m=0, int _y=0) : m(_m), d(_d), y(_y) {}
  friend bool operator==(const Date &x, const Date &y) {return  x.d==y.d;}
};

int main()
{
  Date x = {1,2,3}; // no error; using converting constructor
  x == 1; // no error; converting constructor turns int into Date object
  x == {1,2,3}; // error
}

For x == {1,2,3}, I got the following error:

explicit.cc:16:10: error: expected primary-expression before ‘{’ token
       x=={1,2,3};
          ^

I am wondering why converting constructor doesn't convert list {1,2,3} to Date object? Especially since x == 1 does not result in an error, why does x == {1,2,3}?

like image 277
user27490 Avatar asked Jun 27 '18 21:06

user27490


People also ask

Which is true about conversion constructor?

In C++, if a class has a constructor which can be called with a single argument, then this constructor becomes conversion constructor because such a constructor allows automatic conversion to the class being constructed.

How can we use constructor to convert types?

The above example has the following two conversion constructors: Y(int i) which is used to convert integers to objects of class Y. Y(const char* n, int j = 0) which is used to convert pointers to strings to objects of class Y.

What is a conversion constructor?

A conversion constructor is a single-parameter constructor that is declared without the function specifier explicit . The compiler uses conversion constructors to convert objects from the type of the first parameter to the type of the conversion constructor's class.

Can default constructor have parameters?

A default constructor is a constructor that either has no parameters, or if it has parameters, all the parameters have default values. If no user-defined constructor exists for a class A and one is needed, the compiler implicitly declares a default parameterless constructor A::A() .


2 Answers

To complete Barry's answere, I made a list of all the statements or expressions where a brace-init-list can appear:

  • function call : func({/*...*/},arg2)
  • subscripting: obj[{/*...*/}];
  • explicit type conversion: type{/*...*/}
  • new expression: new type{/*...*/}
  • assignment and compound assignments: a = {/*...*/}; b += {/*...*/};...
  • in the condition of a conditional statement while (atype i={/*.../*})
  • for-range-initializer for(auto it:{/*...*/})
  • return statement: return {/*.../*} (not if the return type is deduced)
  • initializer: atype a{/*...*}; atype b={/*.../*}; or including member initializer: a_memb{/*.../*}
  • default argument void f(atype a={/*.../*})
like image 56
Oliv Avatar answered Oct 17 '22 14:10

Oliv


You might be especially surprised that:

x = {1, 2, 3};            // ok
x == {1, 2, 3};           // error
operator==(x, {1, 2, 3}); // ok

This is because there are just specific places where a braced-init-list (basically, a comma-delimited list of stuff between {}s) is allowed to go in the language. It can go on the right-hand side of = because the rules say it can. It can be used as an argument in a function call expression because the rules say it can. But it cannot be used on either side of the comparison operators because the rules don't allow for it.

I do not know if there is a fundamental reason behind this beyond there probably not being a strong need for it.

like image 17
Barry Avatar answered Oct 17 '22 14:10

Barry