Converting constructors are the same usual constructors which are used to construct objects of class types but they are implicitly used to convert a value to class type.

Consider the following program:

class Int
{
private:
    int m_value{};
 
public:
    Int() = default;
    Int(int value)
        : m_value{value}
    {
    }
 
    void print()
    {
        std::cout << "Int(" << m_value << ")\n";
    }
};
 
void printInt(Int value)
{
    value.print();
}
 
int main(int argc, char const *argv[])
{
    printInt(2);
    return 0;
}

What should happen in this case? Would this be a compilation error as we are passing an int to a function accepting Int class type? Well, this would compile successfully and would print Int(2) as expected result.

What is happening here? Why this working? This is where converting constructor comes into picture. When is passed to the function, value is implicitly copy initialized with instance of Int constructed with constructor Int(int) with value . This constructor which is a normal constructor is called converting constructor.

The instantiation that we are doing up until now like below,

Int i{1};

This is also doing the same thing of implicitly converting to Int class type using the constructor Int(int).

Only one user-defined conversion maybe applied

Consider the following program:

#include <iostream>
#include <string>
 
class Emp
{
private:
    std::string m_name{};
 
public:
    Emp(std::string_view name)
        : m_name{name}
    {
    }
 
    void print()
    {
        std::cout << "Name: " << m_name << "\n";
    }
};
 
void printEmp(Emp emp)
{
    emp.print();
}
 
int main(int argc, char const *argv[])
{
    printEmp("Hemant");
    return 0;
}

It looks like that this program would compile, but it does not compile. The reason being that only one user-defined conversion maybe applied and here two need to be applied.

These two conversions are:

  1. "Hemant" C-style string has to be converted to std::string_view using std::string_view’s converting constructor.
  2. And then std::string_view has to be converted to Emp using Emp’s converting constructor.

Well, this can be solved by either giving it a std::string_view literal as shown below:

printEmp("Hemant"sv);

Or giving it explicit Emp as shown below:

printEmp(Emp{"Hemant"});

Another similar example I have tried to create:

#include <iostream>
 
class Int
{
private:
    int m_value{};
 
public:
    Int() = default;
    Int(int value)
        : m_value{value}
    {
    }
 
    void print()
    {
        std::cout << "Int(" << m_value << ")\n";
    }
};
 
void printInt(Int value)
{
    value.print();
}
 
class Float
{
private:
    Int m_value{};
 
public:
    Float() = default;
    Float(Int value)
        : m_value{value}
    {
    }
 
    void print()
    {
        std::cout << "Float(" << " " << ")\n";
    }
};
 
void printFloat(Float value)
{
    value.print();
}
 
int main(int argc, char const *argv[])
{
    printFloat(1);
    return 0;
}

This will also fail as it requires two conversions:

  1. 1 to Int using Int’s converting constructor.
  2. Int to Float using Float’s converting constructor.

Issue with converting constructors

In this first example, we could able to call printInt with int value and this happens because of converting constructor. However, this can an issue if the intent is not clear. For example, the caller maybe expecting printInt(5) to print 5 but it is printing Int. This is fine for this smaller case but for larger projects, it could be a big problem.

To mitigate this issue we can make constructor explicit so that compiler would not use them for implicit conversions.

References

  1. https://www.learncpp.com/cpp-tutorial/converting-constructors-and-the-explicit-keyword/