class A { public: A():a(0),b(0) {} explicit A(int x): a(x), b(0) {} A(int x, int y): a(x), b(y) {} private: int a,b; }; 

and

 class A { public: explicit A(int x=0, int y=0): a(x), b(y) {} private: int a,b; }; 

Are there any differences? What is better to use?

  • “Which is better to use” - depending on what. - VladD
  • to safely create class A objects and use them - dragonsballZ
  • It depends on what you mean by the concept of "safe creation". - VladD
  • That A a (), A b (x), A c (x, y) worked, there were no implicit type conversions. What else could it be? - dragonsballZ
  • if I find out the difference, I’ll understand that I’m better off) - dragonsballZ

2 answers 2

These two class declarations are not equivalent.

In the second class declaration, the constructor is declared with the explicit function specifier, and this limits the use of this constructor in various situations.

In the first class declaration, only the conversion constructor is declared with the function specifier explicit . This means that you can call other constructors implicitly.

That is, the first declaration provides more options for using the class.

View the following demo.

 #include <iostream> struct A { explicit A( int x = 0, int y = 0 ) : x( x ), y( y ) {} int x; int y; }; struct B { B() : x( 0 ), y( 0 ) {} explicit B( int x ): x( x ), y( 0 ) {} B( int x, int y ): x( x ), y( y ) {} int x; int y; }; void f( const A &a ) { std::cout << "ax = " << ax << ", ay = " << ay << std::endl; } void g( const B &b ) { std::cout << "bx = " << bx << ", by = " << by << std::endl; } int main() { // f( {} ); // f( { 1, 2 } ); g( {} ); g( { 1, 2 } ); } 

Its output to the console:

 bx = 0, by = 0 bx = 1, by = 2 

In this program, the two function calls f commented out, because if uncommented them, the compiler will give an error message.

Another important difference is that one class has only one constructor with a given signature, and the other class has three constructors with different signatures.

View another demo.

 struct A { explicit A( int x = 0, int y = 0 ) : x( x ), y( y ) {} int x; int y; }; struct B { B() : x( 0 ), y( 0 ) {} explicit B( int x ): x( x ), y( 0 ) {} B( int x, int y ): x( x ), y( y ) {} int x; int y; }; struct C { //friend A::A(); friend B::B(); }; int main() { } 

Here in class C you can declare the default constructor of class B as a friend of class C However, you cannot do the same with the default constructor of class A to declare it a friend of class C , since the default constructor in class A has a different signature.

You already have to write

 struct C { friend A::A( int, int ); }; 

this may not be what you would like to receive. That is, if you, for example, wanted a friend to be a constructor, which is called exclusively without arguments.

That is, again, when there are separate designers, then your possibilities are broader.

If we consider not designers, but functions, then the difference is even more significant.

Arguments by default do not affect the type of function. Therefore, for example, if you declare a function as

 void f( int, int = 0 ); 

then despite the default argument and the fact that you can call it as

 f( value ); 

however, its type is void( int, int ) . And this in turn means that you cannot, for example, write

 void h( void f( int ) ) { f( 10 ); } void f( int x, int y = 0 ) { std::cout << "x = " << x << ", y = " << y << std::endl; } // h( f ); 

since the parameter of the function h has the type void( int ) ., and the function used as an argument has the type void( int, int )

If you declare two functions instead of one

 void h( void f( int ) ) { f( 10 ); } void f( int x ) { std::cout << "x = " << x << std::endl; } void f( int x, int y ) { std::cout << "x = " << x << ", y = " << y << std::endl; } 

this challenge

 h( f ); 

will be correct, as there is a function with one parameter.

  • And if you put an explicit with each constructor in 1 case? - dragonsballZ
  • +1, did not know that explicit acts on the constructor with a list. - VladD
  • @dragonsballZ I can not say right away. It takes time to concentrate. :) If something comes to mind, then I will add my answer. - Vlad from Moscow
  • one
    @dragonsballZ If we consider not overloaded constructors, but overloaded functions, then this matters, since when defining the type of a function, the default parameters do not affect the type of the function. That is, if you declare, for example, void f (int) and void f (int, int = 0), these will be two different types, which can make it impossible to use one of the functions, for example, in templates or with the class std: : function. , - Vlad from Moscow
  • @dragonsballZ I completed my answer. :) - Vlad from Moscow

The differences have already been explained by @Vlad from Moscow, I will only suggest, of the two options in question, the third option:

 class A { public: A():A(0, 0) {} explicit A(int x): A(x, 0) {} A(int x, int y): a{x}, b{y} {} private: int a,b; }; 

In my opinion, this option is the best, because it has an explicit constructor with one argument, which is good practice and saves some errors. On the other hand, explicit for constructors who have more or less than one argument, in my opinion, is superfluous. Because accidentally creating an object from more than one argument is problematic, which is why we attributed explicit to the one-argument constructor — protection against random errors.

And most importantly, we have only one constructor that initializes the fields. All the rest operate through it, which allows to minimize initialization errors.