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}
?
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.
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.
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.
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() .
To complete Barry's answere, I made a list of all the statements or expressions where a brace-init-list
can appear:
func({/*...*/},arg2)
obj[{/*...*/}]
;type{/*...*/}
new type{/*...*/}
a = {/*...*/}; b += {/*...*/};...
while (atype i={/*.../*})
for(auto it:{/*...*/})
return {/*.../*}
(not if the return type is deduced)atype a{/*...*}; atype b={/*.../*};
or including member initializer: a_memb{/*.../*}
void f(atype a={/*.../*})
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With