2

代入演算子で*this!=&rhsであることを確認したいと思います。ただし、コンパイルされません。助言がありますか?

template <typename T>
class A {
  public:
      A() {
          std::cout << "Default Constructor" << std::endl;
      }

      A(const T& t) : m_t(t) {
          std::cout << "Templated Constructor" << std::endl;
      }

      template <typename X>
      A( const A<X>& rhs ) : m_t( (static_cast< A<T> >(rhs)).m_t ) {
            std::cout << "Copy Constructor" << std::endl;
      }

      template <typename X>
      const A& operator=( A<X>& rhs) {
            std::cout << "Assignment Operator" << std::endl;
            if (this != static_cast< A<T>* > (&rhs) )
                m_t = rhs.get();
            return *this;
      }

      T get() { return m_t; }
  private:
      T m_t;
};


class base {};
class derived : public base {};


int main()
{
    A<base*> test1;
    A<derived*> test2;
    test1 = test2;  
}
4

5 に答える 5

2

ここでやろうとしていること

if (this != static_cast< A<T>* > (&rhs) )

static_castからを実行しA<derived*>ますA<base*>

内容static_castは次のとおりです。

AがBの基本クラスである場合、タイプAのポインターをタイプBのポインターに明示的に変換できます。AがBの基本クラスでない場合、コンパイラー・エラーが発生します。

A<base*>はの基本クラスではないA<derived*>ため、エラーが発生します。

一般に、がに変換可能であっても、明らかA<T>にの基本クラスになることはありません。そのため、キャストは方程式から外れています。A<X>XT

reinterpret_cast<void*>(&rhs)解決策は、代わりに使用することです。

アップデート

私はこれにもう少し取り組みました。結果は次のとおりです。最初にコードを示し、次にコメントします。

セットアップコード

template <typename T>
class Aggregator {
  public:
      Aggregator() {
          std::cout << "Default Constructor" << std::endl;
      }

      Aggregator(const T& t) : m_t(t) {
          std::cout << "Constructor With Argument" << std::endl;
      }

      Aggregator& operator= (const Aggregator& rhs)
      {
          std::cout << "Assignment Operator (same type)";
          if (this->get() == rhs.get()) {
              std::cout << " -- SKIPPED assignment";
          }
          else {
              T justForTestingCompilation = rhs.get();
          }
          std::cout << std::endl;
          return *this;
      }

      template <class U>
      Aggregator& operator=(const Aggregator<U>& rhs)
      {
          std::cout << "Assignment Operator (template)";
          if (this->get() == rhs.get()) {
              std::cout << " -- SKIPPED assignment";
          }
          else {
              T justForTestingCompilation = rhs.get();
          }
          std::cout << std::endl;
          return *this;
      }

      T get() const { return m_t; }
  private:
      T m_t;
};


class base {};
class derived : public base {};
class unrelated {};

// This is just for the code to compile; in practice will always return false
bool operator==(const base& lhs, const base& rhs) { return &lhs == &rhs; }

これまでに起こっていることに関する重要なポイント:

  1. コピーコンストラクターはありません。実際には、代入ロジックをコピーコンストラクターに移動し、コピーとスワップを使用して代入演算子を実装します。今のところコードを短くしておきます。
  2. 代入演算子は実際には何もしませんがiff、「通常の」、必要に応じて実行するバージョンをコンパイルします。
  3. 2つのアッシング演算子があります。1つ目はに割り当てるAggregate<T>ためのものAggregate<T>で、2つ目はに割り当てるAggregate<T1>ためのものAggregate<T2>です。これはトニーの答えから直接であり、それは「正しい方法」です。
  4. operator==暗黙的に定義されていない型の比較演算子を偽造するための自由があります。具体的には、がプリミティブ型でないAggregate<U>場合にコンパイルするを含むコードに必要です。U

エクササイズコード

ここにすべての楽しみがあります:

int main(int argc, char* argv[])
{
    base b;
    derived d;
    unrelated u;

    Aggregator<base*> aggPB(&b);
    Aggregator<base*> aggPBDerivedInstance(&d);
    Aggregator<derived*> aggPD(&d);
    Aggregator<unrelated*> aggPU(&u);

    Aggregator<base> aggB(b);
    Aggregator<base> aggBDerivedInstance(d); // slicing occurs here
    Aggregator<derived> aggD(d);
    Aggregator<unrelated> aggU(u);

    std::cout << "1:" << std::endl;

    // base* = base*; should compile, but SKIP assignment
    // Reason: aggregate values are the same pointer
    aggPB = aggPB;

    // base = base; should compile, perform assignment
    // Reason: aggregate values are different copies of same object
    aggB = aggB;

    std::cout << "2:" << std::endl;

    // base* = base*; should compile, perform assignment
    // Reason: aggregate values are pointers to different objects
    aggPB = aggPBDerivedInstance;

    // base = base; should compile, perform assignment
    // Reason: aggregate values are (copies of) different objects
    aggB = aggBDerivedInstance;

    std::cout << "3:" << std::endl;

    // base* = derived*; should compile, perform assignment
    // Reason: aggregate values are pointers to different objects
    aggPB = aggPD;

    // base = derived; should compile, perform assignment (SLICING!)
    // Reason: derived is implicitly convertible to base, aggregates are (copies of) different objects
    aggB = aggD;

    std::cout << "4:" << std::endl;

    // base* = derived*; should compile, but SKIP assignment
    // Reason: aggregate values are (differently typed) pointers to same object
    aggPBDerivedInstance = aggPD;

    // base = derived; should compile, perform assignment (SLICING!)
    // Reason: derived is implicitly convertible to base, aggregates are (copies of) different objects
    aggBDerivedInstance = aggD;

    std::cout << "5:" << std::endl;

    // derived* = base*; should NOT compile
    // Reason: base* not implicitly convertible to derived*
    // aggPD = aggPB;

    // derived = base; should NOT compile
    // Reason: base not implicitly convertible to derived
    // aggD = aggB;

    return 0;
}

これは出力します:

Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
Constructor With Argument
1:
Assignment Operator (same type) -- SKIPPED assignment
Assignment Operator (same type)
2:
Assignment Operator (same type)
Assignment Operator (same type)
3:
Assignment Operator (template)
Assignment Operator (template)
4:
Assignment Operator (template) -- SKIPPED assignment
Assignment Operator (template)
5:

それで...私たちはこれから何を学びますか?

  1. テンプレート化された代入演算子は、記述されているように、集約された型の間に暗黙の変換がない限りコンパイルされません。それは良いことです。このコードは、クラッシュするのではなく、コンパイルに失敗します。
  2. 同等性のテストでは、2つの割り当てしか保存されませんでした。どちらもポインターの割り当てです(非常に安価であるため、チェックする必要はありません)。

これは、同等性チェックが不要であり、削除する必要があることを意味します。

ただし、次の場合:

  1. T1T2は同じタイプであるか、暗黙の変換が存在し
  2. T1の代入演算子またはT2の変換演算子は高価であり(これにより、プリミティブがすぐに画像から削除されます)
  3. bool operator== (const T1& lhs, const T2& rhs)上記の割り当て/変換演算子よりも実行コストがはるかに小さいA

次に、同等性をチェックすることは理にかなっているかもしれませんoperator==(あなたが戻ると期待する頻度に依存しますtrue)。

結論

Aggregator<T>ポインタ型(またはその他のプリミティブ)だけで使用する場合は、等価性テストは不要です。

クラス型を構築するために高価なものと一緒に使用する場合は、それらを使用するために意味のある等式演算子が必要になります。異なるタイプでも使用する場合は、変換演算子も必要になります。

于 2011-03-09T02:05:05.067 に答える
2

それが本当に気になる場合はoperator=、キャストを必要としない2番目のテンプレート化されていないものをいつでも持つことができます。冗長性を回避するために、これが!=&rhsの場合、テンプレートバージョンを明示的に呼び出すことができます。適切な演算子がどのように呼び出されるかを示す例:

#include <iostream>

template <class T>
struct X
{
    X& operator=(X& rhs)
    {
        std::cout << "non-template " << (this == &rhs ? "self\n" : "other\n");
    }

    template <class U>
    X& operator=(X<U>& rhs)
    {
        std::cout << "template\n";
    }
};

int main()
{
    X<int> x;
    x = x;
    X<int> y;
    x = y;
    X<double> z;
    x = z;
}
于 2011-03-09T02:31:05.363 に答える
1

あなたの場合、自己割り当てをテストする必要はありません。いくつかのチュートリアルが示唆していることとは反対に、自己代入テストは、一般に、オーバーロードされた代入演算子には必須ではありません。

これは、(実装が不十分な)代入演算子が最初にリソースを解放し、次に右側の演算子のリソースのコピーとなる新しいリソースを作成する場合にのみ必要です。そうして初めて、左側のオブジェクトが右側のオペランド(それ自体)のリソースを同時に解放するため、自己割り当ては壊滅的であることがわかります。

poor_assignment& operator=(const poor_assignment& rhv)
{
    this->Release(); // == rhv.Release() in case of self-assignment
    this->Create(rhv.GetResources()); 
}
于 2011-03-09T16:20:22.580 に答える
0

個人的には、以下が最もエレガントな解決策だと思います。そもそもなぜそれが得られなかったのかわかりません-最初は流血のc++コンパイラを使用していましたが、失敗したようです-しかし、g ++はこれが最もクリーンですか?

人々が私に同意しない場合、私は私の答えを削除し、他の誰かにそれを与えます。A*は実際にはA*を意味しますが、必須であることに注意してください

  template <typename X>
  const A& operator=( A<X>& rhs) {
        std::cout << "Assignment Operator" << std::endl;
        if (this != reinterpret_cast< A* >(&rhs))  
            m_t = rhs.get();               
        return *this;
  }
于 2011-03-09T03:26:24.920 に答える
0

良いバージョン:クラス自体とまったく同じ型をとるコピー代入演算子を実装します。

const A& operator=( A<T>& rhs) {
    std::cout << "copy assignment operator" << std::endl;
    if(this != &rhs)
        m_t = rhs.m_t;
    return *this;
}

「ダーティ」バージョン:各オブジェクトのアドレスをにキャストしintptr_t、プレーンな値を比較します。

template<class X>
const A& operator=( A<X>& rhs) {
    std::cout << "Assignment Operator" << std::endl;
    if((intptr_t)(this) != (intptr_t)(&rhs))
        m_t = rhs.get();
    return *this;
}

編集:実際には、代わりに使用されるコンパイラー生成のコピー代入演算子のため、この2番目のバージョンは機能しません。したがって、自分で実装するだけです。--end editまた、クラス自体からプライベートメンバーにアクセスできるため、
を使用する必要はありません。を使用するrhs.get()だけです。rhs.m_t

于 2011-03-09T02:35:33.130 に答える