Mark constructor explicit to avoid implicit conversion

Question | Dec 23, 2015 | nextptr 

One of the most interesting features of C++ is to implicitly convert the argument of a called function to its formal parameter type. As an example consider:

void Foo(double param);

A call to function Foo as - Foo(4); - would implicitly convert integer 4 to a double.

Implicit conversion also occurs for user defined data types. In case of user defined or non-POD types a single argument constructor behaves as an implicit conversion function. To understand that let's consider following declaration of a class Bar, and a function ProcessBar that accepts a parameter of type Bar:

class Bar { 
  public:
    /* This constructor can be used for
       implicit conversion  */
    Bar(int i); 
};

void ProcessBar(const Bar& b);

In this case a call to ProcessBar() as - ProcessBar(10); - would construct a temporary instance of type Bar that would be passed to ProcessBar. It is because Bar defines an implicit constructor that compiler uses to convert an integer to instance of Bar. Also, note that a multiple argument constructor with default parameters can also be used for implicit conversion.

Implicit conversion is a very useful feature, but it is not always desirable as it can be a source of confusing application behavior and very hard to find defects. It is for this reason C++ has a keyword - explicit - that can be used to mark a constructor explicit. Keyword explicit tells compiler to not use the constructor for implicit conversion. For example declaring Bar's constructor explicit as - explicit Bar(int i); - would prevent us from calling ProcessBar as - ProcessBar(10);.

Solving following problem will help you understand this concept and the importance of keyword explicit:

Consider a data type Point that has members X and Y co-ordinates.

struct Point {

  /* All parameters have default values.
     This constructor can be
     used for implicit conversion. */
  Point(int x=0, int y=0)
     :m_x(x), m_y(y) {}

  int m_x;
  int m_y;
};

We write a function that can calculate slope of a line given any two points on that line. Slope - m - of a straight line is defined as:

This is how it looks like in C++:

double Slope(const Point& p1, const Point& p2) {
  return (double)(p2.m_y-p1.m_y)/(p2.m_x - p1.m_x);
}

Suppose, while calling Slope() we accidentally made a typo that resulted in implicit conversion:

Point p1(4,2);
Point p2(8,3);

/* It is a typo.
   We pass 2 instead of p2 to Slope() */
std::cout << Slope(p1, 2) << std::endl; 

What would be the output of above?