none
C++/CLIの演算子の再定義 RRS feed

  • 質問

  • お世話になります。

    演算子の再定義ですが、C#では、static にする必要があるのですが、C++ではstatic にする必要はありません。
    C++/CLIでのマネージクラスですは、どちらの手法が正しいのでしょうか。
    ※VS2008を使用

    もしstaticにする必要があり場合理由も教えていただけるとありがたいのですが・・・

    下記のクラスでは == を再定義しているクラスで、特に問題なく使用できていると思われるのですが

    ref class CXX
    {
    public:
     int value;
     bool operator==(CXX% o)
     {
      return this == %o;
     }
     bool operator==(CXX^ o)
     {
      if(Object::ReferenceEquals(o,nullptr))
      {
       return false;
      }
      if(Object::ReferenceEquals(this,o))
      {
       return true;
      }
      else
      {
       return Equals(o);
      }
     }
     bool operator!=(CXX% o)
     {
      return this != %o;
     }
     bool operator!=(CXX^ o)
     {
      return !(this == o);
     }

     virtual bool Equals(Object^ o)override
     {
      CXX^ e = dynamic_cast<CXX^>(o);
      return e->value == this->value;
     }
    };

     

    int main(array<System::String ^> ^args)
    {
     CXX o;
     CXX o2;
     Console::WriteLine("{0} = o == nullptr",o==nullptr);
     bool b = o==o;
     Console::WriteLine("{0} = o == o",o==o);
     Console::WriteLine("{0} = o != o",o==o);
     Console::WriteLine("{0} = o2 == o",o2==o);
     o2.value = 100;
     Console::WriteLine("{0} = o2 == o",o2==o);
     Console::WriteLine("{0} = o2 != o",o2 != o);

        return 0;
    }

    結果:
    False = o == nullptr
    True = o == o
    True = o != o
    True = o2 == o
    False = o2 == o
    True = o2 != o

    2010年11月26日 2:36

回答

  • 演算子のオーバーロードを非 static で記述できるのは C++ に合わせたものですね。

    非 static の場合自身が左辺に来る演算しか記述できないという制限、他の言語で呼び出せない可能性がある(実際 C# からは非 static の演算子のオーバーロードは使用できないようです)、といった辺りが問題になり得るところでしょう。

    あと、オーバーロード関数内で this が nullptr になり得るとか。

    • 回答としてマーク TAKAKUN 2010年11月30日 1:23
    2010年11月26日 5:28
  • 非static operatorは対称性があるoperatorを定義することができない。定義できるのは非対称のoperatorだけである。static operatorは対称性のあるoperatorと対称性のないoperatorどちらを定義するか選択できる。
    また、c+2という書き方ができるのに2+cという書き方ができない対称性がないoperator +は、どのように振舞うのか直感で分からない。

    C#の場合、nullにならない値型のみ非static operatorが定義できるようにするのは変だし、かといってc+2がnull参照例外を投げられるというのも変だ。

    だそうです。

    ref class CRef
    {
    public:
    	//以下の2行で対称性を持たせることができる。
    	//どちらかを削れば対称性がなくなる
    	//CRef + 3という書き方ができる。
    	static void operator +(CRef %ref,int val){};
    	//3 + CRefという書き方ができる。
    	static void operator +(int val,CRef %ref){};
    
    	//対称性を持たせることができない。
    	//CRef + 3という書き方ができるが3 + CRefという書き方はできないし、それを実現する手段がない。
    	void operator +(int val){};
    };
    
    

    >どちらの手法が正しいのでしょうか
    正しいかどうかの判断は置いておくとして、C#の設計思想に倣いstaticにするのもひとつの手ですね。

    • 回答としてマーク TAKAKUN 2010年11月30日 1:23
    2010年11月29日 22:47

すべての返信

  • 演算子のオーバーロードを非 static で記述できるのは C++ に合わせたものですね。

    非 static の場合自身が左辺に来る演算しか記述できないという制限、他の言語で呼び出せない可能性がある(実際 C# からは非 static の演算子のオーバーロードは使用できないようです)、といった辺りが問題になり得るところでしょう。

    あと、オーバーロード関数内で this が nullptr になり得るとか。

    • 回答としてマーク TAKAKUN 2010年11月30日 1:23
    2010年11月26日 5:28
  • では何故C#ではstaticが必要か?という観点から見てみると、理解の手助けになると思います。

     

    [Why are overloaded operators always static in C#?]

      http://blogs.msdn.com/b/ericlippert/archive/2007/05/14/why-are-overloaded-operators-always-static-in-c.aspx

    2010年11月26日 11:04
  • ご返事ありがとうございます。

     >非 static の場合自身が左辺に来る演算しか記述できないという制限

    これに関しては、C++も同じでしたよね(でしたっけ)。
    だからしょうがないってことで、

    >他の言語で呼び出せない可能性がある

    これに関しては、確かに参照できませんでした。

     kozzさんに頂いたURL参照したのですが、英文よめなくて翻訳ソフトを使いながら読んだのですが、
    C++の演算子の定義はおかしくて、静的関数にすることで、左辺値の問題を解決できることでstaticになった
    とおもっていいのでしょうか。

     

     

    2010年11月29日 8:59
  • 非static operatorは対称性があるoperatorを定義することができない。定義できるのは非対称のoperatorだけである。static operatorは対称性のあるoperatorと対称性のないoperatorどちらを定義するか選択できる。
    また、c+2という書き方ができるのに2+cという書き方ができない対称性がないoperator +は、どのように振舞うのか直感で分からない。

    C#の場合、nullにならない値型のみ非static operatorが定義できるようにするのは変だし、かといってc+2がnull参照例外を投げられるというのも変だ。

    だそうです。

    ref class CRef
    {
    public:
    	//以下の2行で対称性を持たせることができる。
    	//どちらかを削れば対称性がなくなる
    	//CRef + 3という書き方ができる。
    	static void operator +(CRef %ref,int val){};
    	//3 + CRefという書き方ができる。
    	static void operator +(int val,CRef %ref){};
    
    	//対称性を持たせることができない。
    	//CRef + 3という書き方ができるが3 + CRefという書き方はできないし、それを実現する手段がない。
    	void operator +(int val){};
    };
    
    

    >どちらの手法が正しいのでしょうか
    正しいかどうかの判断は置いておくとして、C#の設計思想に倣いstaticにするのもひとつの手ですね。

    • 回答としてマーク TAKAKUN 2010年11月30日 1:23
    2010年11月29日 22:47
  • kozzさまがありがとうございます。

    理解でき、また勉強になりました。

    ありがとうございました。

     

    2010年11月30日 1:23