メインコンテンツ

キーワード explicit がありません

コンストラクターまたはユーザー定義変換演算子に指定子 explicit がない

説明

この欠陥は、コンストラクターまたはユーザー定義変換演算子の宣言またはクラス内定義で指定子 explicit が使用されていない場合に発生します。指定子 explicit は、別のタイプの変数が現在のクラス タイプに暗黙的に変換されるのを防止します。

欠陥の適用対象は次のとおりです。

  • 1 パラメーターのコンストラクター。

  • パラメーターが 1 つを除いてすべて既定値をもつコンストラクター。

    たとえば、MyClass::MyClass(float f, bool b=true){} です。

  • ユーザー定義変換演算子。

    たとえば、operator int() {} は、現在のクラス型の変数を int 変数に変換します。

リスク

コンストラクターまたは変換演算子 explicit を宣言しなかった場合は、コンパイラがクラス型との間で暗黙的で意図的ではない可能性がある型変換を実行し、予期せぬ結果が生じる可能性があります。

たとえば、クラス型のパラメーターを受け取る関数を他の型の引数で呼び出した場合は、コンストラクターを使用した暗黙的変換が行われる可能性があります。ここでの func の呼び出しにより、int 型から myClass 型への暗黙的変換が行われます。

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

ユーザー定義変換演算子を使用すると、逆暗黙的変換が行われる可能性があります。たとえば、引数としてクラス型を渡したが、関数のパラメーターが別の型だった場合です。ここでの func の呼び出しにより、myClass 型から int 型への暗黙的変換が行われます。

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

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

修正方法

コードの可読性を高め、暗黙的変換を回避するために、コンストラクターまたは変換演算子の宣言またはクラス内定義で、コンストラクターまたは演算子の名前の前にキーワード explicit を配置します。そうすれば、すべての暗黙的変換をコンパイル エラーとして検出し、明示的変換に変換することができます。

すべて展開する

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
}

この例では、MyClass のコンストラクターが explicit と宣言されていません。したがって、呼び出し func(0) により、int から MyClass への暗黙的な変換が実行される場合があります。

修正 — キーワード explicit を使用

1 つの修正方法として、MyClass のコンストラクターを explicit として宣言します。コード内の演算で暗黙的な変換が実行されると、コンパイラでエラーが発生します。したがって、キーワード explicit を使用することで、意図しない型変換をコンパイル段階で検出します。

たとえば、下記の main 関数で、暗黙的な変換を実行するステートメント func(0); を追加すると、コードはコンパイルされません。

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
}

この例では、クラス MonthDay および Year のコンストラクターにキーワード explicit がありません。変数 int から変数 MonthDay および Year への暗黙的な変換が許容されています。

変数 Date が作成され、Date コンストラクターの引数が不適切な順序で使用されている場合でも、暗黙的な変換によりコードはコンパイルされます。月の値と日付の値が入れ替わったことは検出されない可能性があります。

修正 — キーワード explicit を使用

キーワード explicit をクラス MonthDay および Year のコンストラクターに使用した場合、不適切な順序の引数で Date コンストラクターを呼び出すことはできません。

  • Date コンストラクターを変数 int で呼び出しても、コードはコンパイルされません。キーワード explicit が変数 int からの暗黙的な変換を防止するためです。

  • Date コンストラクターを MonthDay および Year へ明示的に変換される引数で呼び出しても、引数の順序が正しくないと、引数タイプの不一致によりコードはコンパイルされません。

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));
}

この例では、変換演算子 operator int32_t()explicit 指定子を使用して定義されておらず、暗黙的変換が可能な状態です。変換演算子 operator bool() は明示的に定義されます。

useBoolVal の呼び出しなどで変数 bool に変換すると、変換演算子内のキーワード explicit によって、MyClass 型から bool 型への明示的変換を実行する必要があることが規定されます。変数 int32_t への変換にはこのような要件はありません。useIntVal の呼び出しでは、暗黙的変換が行われます。

結果情報

グループ: オブジェクト指向
言語: C++
既定値: オフ
コマンド ライン構文: MISSING_EXPLICIT_KEYWORD
影響度: Low

バージョン履歴

R2015b で導入