9.13 — Converting constructors, explicit, and delete

By default, C++ will treat any constructor as an implicit conversion operator. Consider the following case:

Although function makeNegative is expecting a Fraction, we’ve given it an integer instead. However, because Fraction has a constructor willing to take a single integer, the compiler will use this constructor to implicitly convert integer 6 into a Fraction object, which will then be copy initialized into makeNegative() parameter f. The return value is copy-constructed back to main, which then passes it to overloaded operator<<.

Consequently, the above program prints:

Copy constructor called

This implicit conversion works for all kinds of initialization (direct, uniform, and copy).

Constructors eligible to be used for implicit conversations are called converting constructors (or conversion constructors). Prior to C++11, only constructors taking one parameter could be converting constructors. However, with the new uniform initialization syntax in C++11, this restriction was lifted, and constructors taking multiple parameters can now be converting constructors.

The explicit keyword

While doing implicit conversions makes sense in the Fraction case, in other cases, this may be undesirable, or lead to unexpected behaviors:

In the above example, the user is trying to initialize a string with a char. Because chars are part of the integer family, the compiler will use the converting constructor MyString(int) constructor to implicitly convert the char to a MyString. The program will then print this MyString, to unexpected results.

One way to address this issue is to make constructors explicit via the explicit keyword, which is placed in front of the constructor’s name. Constructors made explicit will not be used for implicit conversions:

The above program will not compile, since MyString(int) was made explicit, and an appropriate converting constructor could not be found to convert ‘x’ to a MyString.

However, note that making a constructor explicit only prevents implicit conversions. Explicit conversions (via direct or uniform initialization or explicit casts) are still allowed:

Rule: Consider making your constructors explicit to prevent implicit conversion errors

The delete keyword

In our MyString case, we really want to completely disallow ‘x’ from being converted to a MyString (whether implicit or explicit, since the results aren’t going to be intuitive). One way to partially do this is to add a MyString(char) constructor, and make it private:

However, this constructor can still be used from inside the class (private access only prevents non-members from calling this function).

A better way to resolve the issue is to use the “delete” keyword (introduced in C++11) to delete the function:

When a function has been deleted, any use of that function is considered a compile error.

Note that the copy constructor and overloaded operators may also be deleted in order to prevent those functions from being used.

9.14 -- Overloading the assignment operator
9.12 -- Copy initialization

10 comments to 9.13 — Converting constructors, explicit, and delete

  • davidv

    Here’s a silly question.
    If we add the prototypes for the copy constructor and overloaded operator= to the private section of the class in order to prevent copying, do we actually need to write them down later, given that we’re not really planning to use them?

    • By write them down, I presume you mean implement them. The answer is no, as long as you don’t call them. Don’t forget that private members can still be called by other members of the class, so you’ll have to ensure you don’t use them internally within the class. If you try to do so, you’ll get a linker error.

      • kudleep

        Hi Alex,

        can you give an example , how and in which case , one would need to use copy constructor from other class such as derived one ! The statement - “Sometimes we simply don’t want our classes to be copied at all.” , can you please explain a bit, what i understood is we don’t want compiler to provide a default copy constructor for our program.

        • I’m not sure what you mean by using copy constructors from other classes. The copy constructor is used to instantiate an object of a particular class, so the copy constructor for that class should always be used.

          As for not wanting your classes to be copied, sometimes there are legitimate reasons for this -- eg. doing an assignment would be too expensive, or you simply don’t have time to implement a deep copy and want to prevent people from accidentally using the shallow copy provided by the default copy constructor and default assignment operator the compiler provides for all classes. By making these private, we tell the compiler not to create default versions, thus circumventing the issue.

  • Darren

    Is having the delete syntax for constructors a new(ish) thing added to c++11 (or later)? I’ve never seen it before. So instead of making the copy constructor and copy assignment operator private and not implementing them to make a class un-copyable (non-copyable?) you could just use the delete syntax instead? That is definitely more readable.

  • Sam

    I think Your explanation isn’t fully correct in the first example. Output is the following:
    Copy constructor called
    After looking through the debugger (I might be wrong):
    1. Because there’s a default constructor that "matches literal 6" the function call is allowed.
    2. Right after jumping to the function, default constructor is called. "f" has just been directly initialized.
    3. Copy constructor is called on the "return" statement, to evaluate it and pass back to "main" function. This anonymous object uses its overloaded operator to print its meaning.

  • Chris

    Hi Alex,

    at the section "The delete keyword" section you say "In our MyString case, we really want to completely disallow ‘x’ from being converted to a string (whether implicit or explicit, since the results aren’t going to be intuitive)."
    do you mean converted to an integer? since char is family integer and cannot be converted to string, i guess.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter