Search

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 printFraction() is expecting a Fraction, we’ve given it the integer literal 6 instead. Because Fraction has a constructor willing to take a single integer, the compiler will implicitly convert the literal 6 into a Fraction object. It does this by initializing printFraction() parameter f using the Fraction(int, int) constructor.

Consequently, the above program prints:

6/1

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

Constructors eligible to be used for implicit conversions 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. Similarly, a call to printString(‘x’) causes an implicit conversion that results in the same issue.

One way to address this issue is to make constructors (and conversion functions) explicit via the explicit keyword, which is placed in front of the function’s name. Constructors and conversion functions made explicit will not be used for implicit conversions or copy initialization:

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

However, note that making a constructor explicit only prevents implicit conversions. Explicit conversions (via casting) are still allowed:

Direct or uniform initialization will also still convert parameters to match (uniform initialization will not do narrowing conversions, but it will happily do other types of conversions).

Rule: Consider making your constructors and user-defined conversion member functions 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
Index
9.12 -- Copy initialization

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

  • Mariam

    MyString(char) = delete;

    If any use of this constructor causes compile errors, can we say that this line of code is unnecessary?
    Why this constructor exist at all?

    • nascardriver

      Without deleting this constructor,

      would call `MyString(int)` (`char` is an integral type and can be converted to `int`).
      By adding `MyString(char)`, `mine` tries to call `MyString(char)` instead (Because it's a better match than `MyString(int)`).

  • robinchaz

    Why does this not invoke copy constructor for me? Is it because of copy elision?

    or originally char 'x' is converted to a temporary Mystring object and mine is copy constructed through the temporary object, but there is copy elision and instead mine will be directly initialized by 'x'

    • nascardriver

      That comment was wrong. The copy constructor isn't used in this example.

      • robinchaz

        but without copy elision my comment about the or..... is correct?
        Thanks!!

        • nascardriver

          Initialization only uses the copy constructor if you're using another named object to initialize. Temporaries don't invoke the copy constructor.

  • salah

    Hi,
    I

    Why this code compiled? I used explicit keyword to disallow the constructor to convert any implicit conversion!.

  • Al

    When you say "Consider making your [...] user-defined conversion member functions explicit to prevent implicit conversion errors" do you mean when overloading the typecast as a member function? You're saying to consider using `explicit` in cases like the following, right?

    If that's not the case, what do you mean by "conversion member FUNCTIONS"?

  • Kwonk

    Hi, in the last lesson I asked:
    since

    is

    Does that mean

    is the same as

    And

    is the same as

    ?

    You replied by saying that explicit constructors prevent this example.
    But let's say none of the constructors used are marked as explicit. With this in mind, does that mean intializing an object via the copy constructor or via implicit conversion is treated the same way when you use the copy initialization syntax instead of the uniform/direct initialization syntax?

    • nascardriver

      I don't think there's a difference. That doesn't mean there isn't a difference, but I know how

      would do something different than

      Same for the `otherFraction` example.

  • kavin

    Hi, under 'The explicit keyword' line 14,

    why have you used a pointer type as a parameter ? Can't we just use (const std::string string) as parameter ? Is there any advantage of using a pointer type over std::string type ?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]