2

オーバーロードされた演算子を使用して CLI 値クラス c_Location を作成しようとしていますが、ボックス化に問題があると思います。多くのマニュアルで見られるように、演算子のオーバーロードを実装したので、これは正しいに違いないと確信しています。これは私のコードです:

value class c_Location
{
public:
  double x, y, z;
  c_Location (double i_x, double i_y, double i_z) : x(i_x), y(i_y), z(i_z) {}

  c_Location& operator+= (const c_Location& i_locValue)
  {
    x += i_locValue.x;
    y += i_locValue.y;
    z += i_locValue.z;
    return *this;
  }
  c_Location operator+ (const c_Location& i_locValue)
  {
    c_Location locValue(x, y, z);
    return locValue += i_locValue;
  }
};

int main()
{
  array<c_Location,1>^ alocData = gcnew array<c_Location,1>(2);
  c_Location locValue, locValue1, locValue2;
  locValue = locValue1 + locValue2;
  locValue = alocData[0] + alocData[1];  // Error C2679 Binary '+': no operator found which takes a right-hand operand of type 'c_Location'
}

より長い時間検索した後、値型の配列要素であるためオペランドが参照型であり、関数がアンマネージ参照を取るため値型のみを受け入れることがエラーの原因であることがわかりました。私は今2つの可能性を持っています:

  1. アンボクシング キャストを追加してc_Location、main() の障害のある行を
    locValue = alocData[0] + (c_Location)alocData[1];
  2. operator+ オーバーロードを変更して、パラメーターを参照ではなく値で受け取るようにします。
    c_Location operator+ (const c_Location i_locValue)

どちらのオプションも機能しますが、私が見る限り、どちらにも欠点があります。オプション
1 は、必要に応じて明示的にキャストする必要があることを意味します。
opt 2 は、関数が呼び出し時にパラメーターのコピーを作成するため、パフォーマンスが無駄になることを意味します (ただし、それほどではありません)。

私の質問: 私の失敗分析はまったく正しいですか、それとも失敗には別の理由がありますか?
より良い第 3 の選択肢はありますか?
そうでない場合: 1 と 2 のどちらのオプションが優れていますか? 私は現在、#2を好みます。

4

2 に答える 2

3

ルールはネイティブ C++ とはかなり異なります。

  • CLI は、演算子のオーバーロードがクラスの静的メンバーであることを要求します
  • C++/CLI でconstキーワードを使用することはできますが、そこから得られるメリットはありません。CLI は const ネスの強制をサポートしておらず、それをサポートする .NET 言語も他にほとんどありません。
  • 値型の値の受け渡しは、値によって行う必要があります。これが、そもそも .NET で値型を使用するポイントです。& 参照の使用は非常に面倒です。これは実行時のネイティブ ポインターであり、ガベージ コレクターが調整することはできません。マネージド クラスに埋め込まれた c_Location で演算子のオーバーロードを使用しようとすると、コンパイル エラーが発生します。値コピーのセマンティクスを避けたい場合は、ref class代わりに a を宣言する必要があります。あなたのコードの帽子^。
  • C++/CLI で作成する相互運用型は、他のアセンブリや .NET 言語から使用できるようにパブリックとして宣言する必要があります。それがあなたの意図であるかどうかは完全には明らかではありませんが、通常は C++/CLI コードを書く理由です。

代わりに、値クラスを次のようにすることができます。

public value class c_Location
{
public:
  double x, y, z;
  c_Location (double i_x, double i_y, double i_z) : x(i_x), y(i_y), z(i_z) {}

  static c_Location operator+= (c_Location me, c_Location rhs)
  {
    me.x += rhs.x;
    me.y += rhs.y;
    me.z += rhs.z;
    return me;
  }
  static c_Location operator+ (c_Location me, c_Location rhs)
  {
    return c_Location(me.x + rhs.x, me.y + rhs.y, me.z + rhs.z);
  }
};

テストされていない、近いはずです。main() 内のコードが問題なくコンパイルされることがわかります。

于 2013-08-02T15:04:07.627 に答える
2

TL;DR バージョン:

マネージド コードの場合%は、参照パラメーターで渡すのではなく使用します。&


あなたの診断は完全には正しくありません。ボクシングはあなたの問題とは何の関係もありません。しかし、参照型はある意味でそうです。

「オペランドが参照型であることからエラーが発生していることがわかりました」と言ったとき、あなたは本当に近かったです。オペランドは参照型ではなく値型です。ただし、オペランドが参照型内に格納されているとエラーが発生します。これは、ガベージ コレクション ヒープ (参照型のすべてのインスタンスが配置される場所) 内にあるためです。これは、値型のメンバーを含む独自のオブジェクトだけでなく、配列にも当てはまります。

危険なのは、ガベージ コレクターの実行時に、gc ヒープ上でアイテムを移動できることです。そして、これはネイティブ ポインター ( *) と参照 ( ) を壊します&。なぜなら、それらはアドレスを保存し、それが永遠に同じままであると期待するからです。この問題を処理するために、C++/CLI はトラッキング ポインター ( ^) とトラッキング参照 ( %) を提供します。これらはガベージ コレクターと連携して次の 2 つのことを行います。

  • 使用中に囲んでいるオブジェクトが解放されていないことを確認してください
  • ガベージ コレクタが囲んでいるオブジェクトを移動した場合に新しいアドレスを見つける

C++/CLI から使用する場合は、operator+通常の C++ と同様に、非メンバーを作成できます。

value class c_Location
{
public:
    double x, y, z;
    c_Location (double i_x, double i_y, double i_z) : x(i_x), y(i_y), z(i_z) {}

    c_Location% operator+= (const c_Location% i_locValue)
    {
        x += i_locValue.x;
        y += i_locValue.y;
        z += i_locValue.z;
        return *this;
    }
};

c_Location operator+ (c_Location left, const c_Location% right)
{
    return left += right;
}

欠点は、C# が非メンバーを使用しないことです。C# との互換性のために、非メンバー演算子のように (2 つの明示的なオペランドを使用して) 記述しますが、public static メンバーにします。

value class c_Location
{
public:
    double x, y, z;
    c_Location (double i_x, double i_y, double i_z) : x(i_x), y(i_y), z(i_z) {}

    c_Location% operator+= (const c_Location% i_locValue)
    {
        x += i_locValue.x;
        y += i_locValue.y;
        z += i_locValue.z;
        return *this;
    }

    static c_Location operator+ (c_Location left, const c_Location% right)
    {
        return left += right;
    }
};

operator+=C# はとにかくそれを認識しないため、これについて心配する必要はありませんoperator+。結果を使用して元のオブジェクトに代入します。


doubleまたはのようなプリミティブ型の場合、管理対象オブジェクト内に格納されているプリミティブ型のインスタンスへの参照が必要な場合にのみint使用する必要がある場合があります。%

double d;
array<double>^ a = gcnew darray<double>(5);
double& native_ref = d; // ok, d is stored on stack and cannot move
double& native_ref2 = a[0]; // error, a[0] is in the managed heap, you MUST coordinate with the garbage collector
double% tracking_ref = d; // ok, tracking references with with variables that don't move, too
double% tracking_ref2 = a[0]; // ok, now you and the garbage collector are working together
于 2013-08-02T14:04:26.977 に答える