C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++のexplicitキーワードとは?暗黙の型変換を防ぐ使い方を解説

この記事では、C++におけるexplicitキーワードの効果について詳しく解説します。まずは以下のサンプルコードを見て、出力結果を予想してみてください。

例1:explicitを使わない場合(暗黙の型変換が発生する)

#include <iostream>
using namespace std;
class Point {
   private:
      double x, y;
   public:
      Point(double a = 0.0, double b = 0.0) : x(a), y(b) {
         // コンストラクタ
      }
      bool operator==(Point p2) {
         if(p2.x == this->x && p2.y == this->y)
         return true;
         return false;
      }
};
int main() {
   Point p(5, 0);
   if(p == 5)
      cout << "They are same";
   else
      cout << "They are not same";
}

出力結果

They are same

このコードは問題なくコンパイルされ、「They are same」と出力されます。

その理由は、引数を1つだけ指定して呼び出せるコンストラクタは「変換コンストラクタ(conversion constructor)」として機能するからです。この例では、p == 5という比較の際に、int型の値5がPointクラスのコンストラクタを通じて暗黙的にPoint型へと変換され、operator==が呼び出されています。

しかし、このような暗黙の変換は、開発者の意図しない動作を引き起こし、予期せぬバグの原因となる可能性があるため、避けたい場面も多くあります。

例2:explicitキーワードで暗黙の変換を禁止する

暗黙の型変換を防ぎたい場合は、コンストラクタにexplicit修飾子を付けます。explicitが付いたコンストラクタは明示的な呼び出しにのみ使用でき、暗黙の変換には使われなくなります。

#include <iostream>
using namespace std;
class Point {
   private:
      double x, y;
   public:
      explicit Point(double a = 0.0, double b = 0.0) : x(a), y(b) {
         // コンストラクタ
      }
      bool operator==(Point p2) {
         if(p2.x == this->x && p2.y == this->y)
         return true;
         return false;
      }
};
int main() {
   Point p(5, 0);
   if(p == 5)
      cout << "They are same";
   else
      cout << "They are not same";
}

出力結果

[Error] no match for 'operator==' (operand types are 'Point' and 'int')
[Note] candidates are:
[Note] bool Point::operator==(Point)

explicitキーワードを付けたことで、p == 5の部分でint型からPoint型への暗黙の変換が行われなくなり、コンパイルエラーが発生します。これにより、意図しない型変換によるバグをコンパイル段階で検出できるようになります。

例3:明示的なキャストであれば変換可能

explicitキーワードを付けても、明示的なキャスト(explicit casting)を使えば、値をPoint型に変換することは引き続き可能です。つまり、explicitは「暗黙の変換」だけを禁止し、「明示的な変換」までは禁止しないのです。

#include <iostream>
using namespace std;
class Point {
   private:
      double x, y;
   public:
      explicit Point(double a = 0.0, double b = 0.0) : x(a), y(b) {
         // コンストラクタ
      }
      bool operator==(Point p2) {
         if(p2.x == this->x && p2.y == this->y)
         return true;
         return false;
      }
};
int main() {
   Point p(5, 0);
   if(p == (Point)5)
      cout << "They are same";
   else
      cout << "They are not same";
}

出力結果

They are same

まとめ

  • 引数を1つだけ(またはデフォルト引数により1つで呼び出せる)取るコンストラクタは、変換コンストラクタとして暗黙の型変換を許可する
  • コンストラクタにexplicitを付けると暗黙の変換が禁止され、誤用はコンパイルエラーとして検出される
  • (Point)5のような明示的なキャストによる変換は引き続き可能
  • C++11以降では、変換演算子(conversion operator)にもexplicitを適用できる

予期しない暗黙の変換を防ぎ、より安全で堅牢なコードを書くために、単一引数を取るコンストラクタにはexplicitを付けることが、現代のC++プログラミングにおけるベストプラクティスとされています。

  1. C++でcomplexクラスを活用したジオメトリ(幾何計算)の実装方法

    この記事では、C++のSTLに含まれるcomplexクラスを使ってPointクラスを作成し、幾何学関連の問題に応用する方法を解説します。複素数は、STLのcomplexクラス(#include <complex>)で簡単に扱うことができます。 Pointクラスの定義 complexをpointとして扱うために、complex<double>に「point」という別名(typedef)を付けます。さらに、complexクラスのreal()をx、imag()をyとしてマクロ定義することで、Pointクラスと同様の操作が可能になります。 #include <comple

  2. C++で点を別の点を中心として回転させる方法

    原点を中心とした点の回転 点Xを原点を中心として角度θだけ反時計回りに回転させるには、以下の式を使用します。 原点を中心にθだけ反時計回りにXを回転する式: X * polar(1.0, θ) ここで使われている polar 関数は、<complex> ヘッダーファイルで定義されている複素数用の関数で、大きさ(絶対値)と位相角から複素数を生成するために使用されます。polar(mag, angle) を呼び出すと、対応する複素数が返されます。複素数を平面上の点として扱うことで、回転のような幾何学的な操作を簡潔に記述できるのがポイントです。 点Yを中心とした点Xの回転 ある点を別の