Main Content

Missing explicit keyword

Constructor or user-defined conversion operator missing the explicit specifier

Description

This defect occurs when the declaration or in-class definition of a constructor or user-defined conversion operator does not use the explicit specifier. The explicit specifier prevents implicit conversion from a variable of another type to the current class type.

The defect applies to:

  • One-parameter constructors.

  • Constructors where all but one parameters have default values.

    For instance, MyClass::MyClass(float f, bool b=true){}.

  • User-defined conversation operators.

    For instance, operator int() {} converts a variable of the current class type to an int variable.

Risk

If you do not declare a constructor or conversion operator explicit, compilers can perform implicit and often unintended type conversions to or from the class type with possibly unexpected results.

The implicit conversion using a constructor can occur, for instance, when a function accepts a parameter of the class type but you call the function with an argument of a different type. The call to func here causes an implicit conversion from type int to myClass:

class myClass {}{
  ...
  myClass(int) {...}
};
void func(myClass);
func(0);

The reverse implicit conversion can occur when using a user-defined conversion operator. For instance, you pass the class type as argument but the function has a parameter of a different type. The call to func here causes an implicit conversion from type myClass to int:

class myClass {} {
  ...
  operator int() {...}
};
myClass myClassObject;

void func(int) {...}
func(myClassObject);

Fix

For better readability of your code and to prevent implicit conversions, in the declaration or in-class definition of the constructor or conversion operator, place the explicit keyword before the constructor or operator name. You can then detect all implicit conversions as compilation errors and convert them to explicit conversions.

Examples

expand all

class MyClass {
public:
    MyClass(int val);
private:
    int val;
};

void func(MyClass);

void main() {
    MyClass MyClassObject(0);

    func(MyClassObject);   // No conversion
    func(MyClass(0));      // Explicit conversion
    func(0);               // Implicit conversion
}

In this example, the constructor of MyClass is not declared explicit. Therefore, the call func(0) can perform an implicit conversion from int to MyClass.

Correction — Use explicit Keyword

One possible correction is to declare the constructor of MyClass as explicit. If an operation in your code performs an implicit conversion, the compiler generates an error. Therefore, using the explicit keyword, you detect unintended type conversions in the compilation stage.

For instance, in function main below, if you add the statement func(0); that performs implicit conversion, the code does not compile.

class MyClass {
public:
    explicit MyClass(int val);
private:
    int val;
};

void func(MyClass);

void main() {
    MyClass MyClassObject(0);

    func(MyClassObject);   // No conversion
    func(MyClass(0));      // Explicit conversion
}
class Month {
    int val;
public:
    Month(int m): val(m) {}
    ~Month() {}
};

class Day {
    int val;
public:
    Day(int d): val(d) {}
    ~Day() {}
};

class Year {
    int val;
public:
    Year(int y): val(y) {}
    ~Year() {}
};

class Date {
    Month mm;
    Day dd;
    Year yyyy;
public:
    Date(const Month & m, const Day & d, const Year & y):mm(m), dd(d), yyyy(y) {}
};

void main() {
    Date(20,1,2000); //Implicit conversion, wrong argument order undetected
}

In this example, the constructors for classes Month, Day and Year do not have an explicit keyword. They allow implicit conversion from int variables to Month, Day and Year variables.

When you create a Date variable and use an incorrect argument order for the Date constructor, because of the implicit conversion, your code compiles. You might not detect that you have switched the month value and the day value.

Correction — Use explicit Keyword

If you use the explicit keyword for the constructors of classes Month, Day and Year, you cannot call the Date constructor with an incorrect argument order.

  • If you call the Date constructor with int variables, your code does not compile because the explicit keyword prevents implicit conversion from int variables.

  • If you call the Date constructor with the arguments explicitly converted to Month, Day and Year, and have the wrong argument order, your code does not compile because of the argument type mismatch.

class Month {
    int val;
public:
    explicit Month(int m): val(m) {}
    ~Month() {}
};

class Day {
    int val;
public:
    explicit Day(int d): val(d) {}
    ~Day() {}
};

class Year {
    int val;
public:
    explicit Year(int y): val(y) {}
    ~Year() {}
};

class Date {
    Month mm;
    Day dd;
    Year yyyy;
public:
    Date(const Month & m, const Day & d, const Year & y):mm(m), dd(d), yyyy(y) {}
};

void main() {
    Date(Month(1),Day(20),Year(2000)); 
    // Date(20,1,2000); - Does not compile
    // Date(Day(20), Month(1), Year(2000)); - Does not compile
}
#include <cstdint>

class MyClass {
public:
    explicit MyClass(int32_t arg): val(arg) {};
    operator int32_t() const { return val; }
    explicit operator bool() const {
        if (val>0) {
          return true;
        }
        return false;
     } 
private:
    int32_t val;
};

void useIntVal(int32_t);
void useBoolVal(bool);

void func() {
    MyClass MyClassObject{0};
    useIntVal(MyClassObject); 
    useBoolVal(static_cast<bool>(MyClassObject));
}

In this example, the conversion operator operator int32_t() is not defined with the explicit specifier and allows implicit conversions. The conversion operator operator bool() is defined explicit.

When converting to a bool variable, for instance, in the call to useBoolVal, the explicit keyword in the conversion operator ensures that you have to perform an explicit conversion from the type MyClass to bool. There is no such requirement when converting to an int32_t variable. In the call to useIntVal, an implicit conversion is performed.

Result Information

Group: Object oriented
Language: C++
Default: Off
Command-Line Syntax: MISSING_EXPLICIT_KEYWORD
Impact: Low

Version History

Introduced in R2015b