コンパイラーに牛と長靴を区別させる

ご存知のように、C ++およびその祖先Cでは、typdef命令は新しい型を作成せず、既存の型のエイリアスのみを作成します。 そして、弱く型付けされたCでこれが問題にならなかった場合、厳密なC ++では、特に初心者にとっては困惑し、プログラムのとらえどころのないバグにつながります。



この記事では、この不快な機能を回避しようとします。



例を見てみましょう。



typedef unsigned int galosh_count_t; typedef unsigned int cow_count_t; void print (galosh_count_t count) { std::cout << "   " << count << "  !" << std::endl; } void print (cow_count_t count) { std::cout << "   " << count << " !" << std::endl; } void print (galosh_count_t galosh_count, cow_count_t cow_count) { std::cout << "   " << galosh_count << "    " << cow_count << " !" << std::endl; } int main (int, char*[]) { galosh_count_t galosh_count = 10; cow_count_t cow_count = 15; print (cow_count, galosh_count); //   ,   ,     ,      print (galosh_count); // ,     print (cow_count); // ,     }
      
      







この例では、2つのコンパイルエラーと1つの論理エラーがあります。 また、1つの引数で印刷関数をオーバーロードできない場合は、引数の順序を間違えると、プログラムに完全に幻想的でわかりにくいバグが生じる可能性があります。 コードを修正することにより、そのようなエラーも検出することが非常に難しくなり、関数内の引数が増えるほど難しくなります。



コンパイラーに牛と雨靴を区別するように教えます



だから、エイリアスの代わりに新しいタイプを作成するためにtypedefを取得する方法は? もちろん、テンプレートの助けを借りて。



試行番号1。

 template <class T> class strong_type { public: explicit strong_type (const T& val) : _val (val) {} strong_type& operator = (const T& val) {_val = val; return *this;} private: T _val; }; typedef strong_type<unsigned int> galosh_count_t; typedef strong_type<unsigned int> cow_count_t;
      
      







動作しません。galosh_count_tとcow_count_tはすべて同じタイプです。 コンパイラは、unsigned intパラメータを使用して、strong_typeクラスを1回だけインスタンス化します。



コンパイラに新しい型を作成させるために、テンプレートに別の引数を追加します。



試行番号2。

 template <class T, class Tag> class strong_type { public: explicit strong_type (const T& val) : _val (val) {} strong_type& operator = (const T& val) {_val = val; return *this;} private: T _val; }; typedef strong_type<unsigned int, class TAG_galosh_count_t> galosh_count_t; typedef strong_type<unsigned int, class TAG_cow_count_t> cow_count_t;
      
      







したがって、galosh_count_tとcow_count_tの2つの異なるタイプがあります。 コンパイラーは、引数の順序を突然混乱させ、逆にオーバーロードされた関数のあいまいさについて文句を言わない場合、汚い私に誓います。



TAG_galosh_count_tクラスとTAG_cow_count_tクラスを定義する必要はありません。これらは単に一意のタグとして使用されます。



ただし、この例が最終的に機能するには、<<演算子をリロードする必要があります。



 template <class T, class Tag> class strong_type { public: explicit strong_type (const T& val) : _val (val) {} strong_type& operator = (const T& val) {_val = val; return *this;} template <class Stream> Stream& operator << (Stream& s) const { s << _val; return s; } private: T _val; }; typedef strong_type<unsigned int, class TAG_galosh_count_t> galosh_count_t; typedef strong_type<unsigned int, class TAG_cow_count_t> cow_count_t;
      
      







もちろん、実際のプログラムでは、算術演算子と論理演算子をオーバーロードして、牛と雨靴を追加、減算、および比較できるようにする必要があります。 自分で処理できると思います。



All Articles