C++暗黙クラスタイプ変換およびexplicitキーワードの使用

3457 ワード

1.暗黙型変換
C/C++の基本タイプの自動タイプ変換は、下のコードなど、よく見られる状況です.
int i ;
float j ;
......
j = i;

実は、これは暗黙的なタイプ変換です.整形されたiを浮動小数点型のjに割り当てると、コンパイラは自動的に変換を完了する.暗黙的なタイプの変換があって、私たちは多くのコードを書くことを少なくして、多くの面倒を省きました.
2.暗黙クラス型変換
C++のクラスは、ユーザがカスタマイズしたデータ型と見なすことができる.基本データ型と同様にC++クラスもデータ型である以上,C++クラスに暗黙型変換をサポートさせることは自然な考え方となる.
C++ルールでは、単一の実パラメータを呼び出すことができるコンストラクション関数は、パラメータタイプからクラスタイプへの暗黙的な変換を定義します.たとえば、下のコード:
class Student
{
public: 
    Student() { }
    Student(int age) { }
};

class Teacher
{
public:
    Teacher() { }
    Teacher(int age, string name="unkown") { }  
};

Studioクラスのコンストラクション関数は、オブジェクトをコンストラクションするのに1つの実パラメータしか必要ありません.Teacherクラスのコンストラクション関数には2つのパラメータがありますが、2番目のパラメータはデフォルト値を提供するため、1つのパラメータでオブジェクトを構築することもできます.したがって,C++ルールに従って,この2つのクラスの構造関数は暗黙的な変換機能を実現した.下のコードも完全にコンパイルできます
Student foo;
Teacher bar;

foo = 12;    //    
bar = 40;    //    

上記の暗黙的なタイプ変換もコンパイラによって行われ、コンパイラは構造関数によって一時的なオブジェクトを構築し、fooとbarにコピーします.
このように使うと、基本的なタイプの自動変換と同じようにさっぱりしていて、多くのコードを省くことができます.しかし、何事にも両面性があり、便利なものほど間違いを犯しやすい.どうしていつもコンパイラがあなたのために構築したコードがあなたが実現したい論理であることを保証することができますか?コードスペルミスが発生すると、コード実装は自分の論理と一致せず、コンパイラはエラーを報告しないので、Debugの場合はさらに時間と労力がかかります.
暗黙的なクラスタイプ変換の欠点について、ネット上で広く伝わっているのは以下の2つの例である.
例1:スペルミス
Array a[10];
Arrayb[10];
for(int i=0;i<10;i++) {
     if(a==b[i]) {        //   a[i],       
                              //     
      }
}

この例では一部のコードは省略するが、原文は参照文書[1]を参照する.
簡単に言えば、Arrayクラスには1つの整形パラメータのみを受信できる構造関数が定義されているため、if(a==b[i])でスペルミスが発生した場合、コンパイラはエラーを報告するのではなく、自動的にb[i]から1つのArrayタイプの一時オブジェクトを暗黙的に変換し、aと比較する.
例2:論理エラー
// A simple class
class A {};

// Another simple class with a single-argument constructor for class A
class B
{
public:
    B() {}
    B(A const&) {}
};

// A function that expects a 'B'
void f(B const&) {}

int main()
{
    A obj;
    f(obj); // Spot the deliberate mistake
}

この例では論理エラーは少し強引なようですが、私の本来の論理はAクラスのオブジェクトを通じてBクラスのオブジェクトを構築し、f()関数に使用することです.もしそうなら、少なくとも論理が厳密ではないと言えるでしょう.少なくとも大部分のコードを読む人は驚くだろう.f()関数はBクラスのオブジェクトを処理しなければならないが、Aクラスのオブジェクトをパラメータとして与え、コードコンパイルは全く問題ない.
コードコンパイルに問題がないのは、BクラスがAクラスオブジェクトの参照値をパラメータとして受け入れる構造関数を含み、暗黙的な変換規則を満たしているからである.したがって,Aクラスオブジェクトobjをパラメータとしてf()関数に渡すと,コンパイラは暗黙的変換規則を呼び出し,一時的なBクラスオブジェクトを生成してf()関数に渡す.上記例の完全な説明は、参照文書[2]を参照することができる.
暗黙的な変換が強すぎると思うかもしれませんが、問題は、柳を挿すのではなく、毎回意図的に書いたコードであることを保証できるかどうかです.
3.Google C++プログラミング仕様暗黙型変換の提案
暗黙型変換の長所と短所については、Google C++プログラミング仕様について詳しく説明しています.その長所は文法が簡潔で、手間が省けることです.欠点は多いです.
  • タイプが一致しないエラーを隠しやすい.
  • コードはもっと読みにくいです.
  • 単一パラメータを受信する構造方法は、意外にも暗黙的なタイプ変換として使用される可能性がある.
  • は、そのタイプがどのような場合に暗黙的に変換されるべきかを明確に定義することができず、コードが難解になる.

  • Googleの結論は、単一のパラメータを受信できるコンストラクション関数にはexplicitタグを付ける必要があり、暗黙的なクラスタイプ変換を禁止する必要がある(コピーと移動コンストラクション関数を除く.この2つはタイプ変換を実行しないため).
    Googleプログラミング仕様によれば、上記の例では、単一のパラメータを受信できる構造関数は、以下の形式で宣言されるべきである.
    class Student
    {
    public: 
        Student() { }
        explicit Student(int age) { }
    };
    
    class Teacher
    {
    public:
        Teacher() { }
        explicit Teacher(int age, string name="unkown") { } 
    };
    

    これにより、クラスタイプの暗黙的な変換が禁止され、コンパイラは類似コードをコンパイルするときに直接エラーを報告し、開発者に自分のコードが論理に合っているかどうかを確認するように注意します.
    リファレンスドキュメント
  • [1]c++暗黙的変換による問題
  • [2] Why You Should Always Use explicit Constructors
  • [3] Google C++ Style Guide