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

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

  • topherno

    Hey Alex,

    Regarding prevention of implicit conversions via explicit, why on earth is "Direct or uniform initialization will also still convert parameters to match" still allowed? I tested it out with clang, and it's true. Because as a result, explicit only prevents implicit conversions some of the time, and all you have to do to unwittingly get around it is use either direct or uniform initialization. And uniform initialization is even what we're supposed to be using when writing modern C++ code. Am I missing something or is this some known shortcoming that's going to be addressed in future C++ versions? Thanks.

  • Passionate_programmer

    Can we prevent 'elision' in copy constructors from happening by putting 'explicit' or 'delete' keyword in front of copy constructor even in C++17? In other words, If I wanted the code below call both default and copy constructors, what should I do in C++17?

    >>One way to address this issue is to make constructors (and conversion functions) explicit via the explicit keyword
    What kind of functions are 'conversion functions'? Like 'operator typecast'?

    • nascardriver

      You can't prevent this kind of copy elision. It's a mandatory optimization.

      > What kind of functions are 'conversion functions'? Like 'operator typecast'?
      Yes, typecast operators

    • Fan

      More info here:

      https://en.cppreference.com/w/cpp/language/copy_elision

      Quote:

      Under the following circumstances, ... The copy/move constructors need not be present or accessible:

      * ...

      * In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:

      ... (Your example fits here)

  • Rushhahb

    >>Because Fraction has a constructor willing to take a single integer, the compiler will implicitly convert the literal 6 into a Fraction object.

    Does the sentence above imply that this constructor conversion feature works in this situation because we have default parameters (0,1) for the constructor? I didn't get the part that says 'Because Fraction has a constructor willing to take a single integer'!

    >>It does this by initializing printFraction() parameter f using the Fraction(int, int) constructor.
    Do we have initialization here because of having pass by reference not by value?

    • nascardriver

      It works because we have a constructor that works with 1 argument. It doesn't matter if that constructor exists because there's one with default arguments or because we have a constructor with 1 parameter that isn't defaulted.

      We have initialization here, because `printFraction` wants a `Fraction` but we're giving it an `int`. The `int` is used to construct a temporary `Fraction`.

  • 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]