0

C++ でのカンマ演算子のオーバーロードに関する投稿 (質問) が多数あります。ほとんどの回答では、コンマ演算子のオーバーロードを使用しないようにアドバイスされています。Matlab 言語に非常によく似た構文で C++ ライブラリを作成したいと考えています。基本オブジェクトは Matrix MX です。ライブラリのエンドユーザーが次のような式を記述できるようにしたい:

MX a = b(i);// get b elements at indices i 
b(i,j)= a; // set elements of b at indices i,j.

MXオブジェクトへのポインターを保存し、インデックスi、jオブジェクトも保存するプロキシクラスを使用して、セッターとゲッターを上記のように機能させる方法について考えています。たとえば、b(i,j) は Proxy オブジェクト ProxMX(b,i,j) を作成します。次に、ProxMX を MX およびその逆 (演算子 = を使用) に割り当てるメソッドを定義します。これは、b の要素を取得および設定するという難しい仕事を行います。

次のような関数呼び出しを行うには助けが必要です:

(x,y,z)= ff(a,b,c) 

a、b、c は入力引数 (MX オブジェクト) で、x、y、z は出力引数です。上記の構文が不可能な場合は、次のような構文を考えることができます。

ff((a,b,c), (x,y,z) )

私はこのテストコードを書き始めました:

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;




class MX {// Matrix 

public:
    MX(double va) {
        elem=va;// only one double for the moment to test the syntaxe
    }
    MX &operator ()(MX idx){ // get & set MX(i)
        return *this;//
    };
    MX &operator ()(MX idx1,MX idx2) { // get set MX(i,j)
        return *this;
    } ;
    friend ostream &operator<<(ostream &stream, MX a);

    double elem;

};

ostream &operator<<(ostream &stream, MX a)
{
  stream << a.elem ;

  return stream;
}

typedef vector<const MX > MXR;
class ArgList { // Proxy
public:
    //ArgList(const MX& a){
    //  data.push_back(a);
    //}
    ArgList() {};

    ArgList& operator , (const MX &a){
        data.push_back(a);
        return *this;
   }
   ArgList& operator =(ArgList& ins){
        for (int i=0 ;i <ins.data.size();i++)
            (this->data[i]).elem=ins.data[i].elem;
        return *this;
   };
    MXR data; 
};


ArgList operator , (const MX& a, const MX& b){
    ArgList out;    
    out.data.push_back(a);
    out.data.push_back(b);
    return out;
   }

ArgList ff(ArgList argins)
{

    int n = argins.data.size();
    MX a= argins.data[0];
    MX b= argins.data[1];
    MX x(a.elem+1.0);
    MX y(b.elem+10.0);
    MX z(a.elem+b.elem);
    return ( x, y , z);

}
void gg(ArgList argins, ArgList &argout)
{

    int n = argins.data.size();
    MX a= argins.data[0];
    MX b= argins.data[1];
    MX x(a.elem+1.0);
    MX y(b.elem+10.0);
    MX z(a.elem+b.elem);
    argout = ( x, y , z);

}
int _tmain(int argc, _TCHAR* argv[])
{
    MX a(1.0);MX b(2.0);MX c(3.0);
    MX aa = a(MX(3.0));
    aa(MX(2.0),MX(3.0))=MX(5.0);
    cout << "a=" << a << ",b=" << b << ",c=" << c << endl;
    MX x(0.0);MX y(0.0);MX z(0.0);
    cout << "x=" << x << ",y=" << y << ",z=" << z << endl;
    (x,y,z)= ff((a , b, c ));
    cout << "x=" << x << ",y=" << y << ",z=" << z << endl;
    gg((a,b,c) , (x,y,z));
    cout << "x=" << x << ",y=" << y << ",z=" << z << endl;
    return 0;
}

このコードは、VS2010 Express を使用してエラーなしでコンパイルおよび実行されます:)。しかし、ベクトルにオブジェクトのコピーを作成する代わりに、変数への参照を ArgList に保存する必要があるため、期待どおりの結果が得られません。オブジェクトへの参照のコンテナーとして std::vector を使用できないことはわかっています。

これらの式を書き込み可能にし、C++ コードで作業するためのヘルプ。ありがとう。

4

3 に答える 3

2

C ++ 11では、タプルを使用してこれを行うことができます。

std::tuple<some_type, another_type, yet_another_type> ff(...);

some_type x;
another_type y;
yet_another_type z;

std::tie(x,y,z) = ff(a,b,c);

コンパイラがそれをサポートしていない場合、Boost.Tupleライブラリは非常に似ていますが、可変個引数テンプレートからのサポートがないとさらに制限される可能性があります。

のような構文を本当にサポートしたい場合は(x,y,z) = ff(a,b,c);、Matlabで適切に見えても、C++プログラマーにとってはかなり混乱する可能性があります。値ではなくポインタを含み、結果への非参照からこれらのポインタを初期化し、 (または他の値のコレクション)とを受け取る代入演算子を持つ、と同様の別の型ArgList(おそらくのようなものと呼ばれる)が必要です。ポインタを介して各要素を割り当てます。RefListconstArgList

また、Boost.Assignmentを調べて、そのような演算子のオーバーロードをどのように機能させることができるかを確認することもできます。少し面倒です。特に、組み込み型のみに作用する演算子をオーバーロードすることはできません。これにより、そのアプローチの有用性が制限されます。個人的には、C ++ 11がオプションの場合は、代わりに可変個引数テンプレートを使用します。

于 2012-04-24T09:55:49.100 に答える
2

私はC++ 11を使用してソリューションの概要を説明しています(コードもテストされています)が、これはC ++ 03で実行可能です(Boost.Tupleなどを使用):

// Base case
template<typename Lhs, typename Rhs>
std::tuple<Lhs&, Rhs&>
operator,(Lhs& lhs, Rhs& rhs)
{
    return std::tie(lhs, rhs);
}

// General case when we already have a tuple
template<typename... Lhs, typename Rhs>
std::tuple<Lhs..., Rhs&>
operator,(std::tuple<Lhs...>&& lhs, Rhs& rhs)
{
    return std::tuple_cat(lhs, std::tie(rhs));
}

使用法は次のようになります (この演算子が にあると仮定しますnamespace ns):

// Declaration: note how ff must return a tuple
std::tuple<X, Y, Z> ff(A, B, C);

A a = /* ... */;
B b = /* ... */;
C c = /* ... */;
X x; Y y; Z z;

using ns::operator,;
// brackets on the left-hand side are required
(x, y, z) = ff(a, b, c);

添付の注意事項は次のとおりです。

  • operator,これまで見てきたように、スコープを導入するには using 宣言が必要です。タイプXYが(ADL を有効にするために)Zの同じスコープ内にある場合でも、そうではありません。(ただし、C++03 で行うのはそれほど便利ではありません) 適切な名前空間に独自のタプルを配置して、ADL を簡単な方法で使用することができます。でも:operator,std::tupletemplate<typename... T> struct tuple: std::tuple<T...> { using std::tuple<T...>::tuple; };

  • オーバーロードされた演算子は、常に少なくとも 1 つのユーザー定義型で動作する必要があります。したがって、型Xと両方がたまたままたはのYような型である場合は、デフォルトの が取得されます。このようなことに対する通常の解決策は、代わりにwhereが適切な名前空間で型を返すようなことをクライアントに要求することです (ADL の目的で)。おそらく、そのような型は、タプルと同じ簡単なハックを使用して (または C++03 の場合は Boost バージョン) で実装できます。( の追加のオーバーロードが必要です。)intdoubleoperator,(ref(x), ref(y), z) = ff(a, b, c);refstd::reference_wrapperoperator,

全体として、次のような場合は(醜い回避策を使用して)多くの作業が必要です

/* declaration of ff and variable definitions the same as before */
std::tie(x, y, z) = ff(a, b, c);

多分

/* this skips unnecessary default constructions of x, y, z */
auto tuple = ff(a, b, c);
using std::get;
auto& x = get<0>(tuple);
auto& y = get<1>(tuple);
auto& z = get<2>(tuple);

すぐに使用できます (Boost.Tuple を使用した C++03 でも)。この問題に関する私のアドバイスは次のとおりです (軽視する意図はありません):シンプルに、ばかにしてください! そしてそれを使用すること。

于 2012-04-24T09:53:57.620 に答える
0

ご提案とフィードバックをいただき、ありがとうございます。これは、2 つのプロキシを使用した実際の例です。ArgList には MX オブジェクトへのポインタが含まれ、ArgCopyList には MX オブジェクトのコピーが含まれます。

matlab ユーザーの観点から見ると、c++ 構文(x,y,...)=myfunc((a,b,...))は matlab 式に非常に似ています[x,y,...]=myfunc(a,b,...)。しかし、私の第一印象は、この種の関数呼び出しはまったく効率的ではないということです。なぜなら、戻り値がコピーされるためです。これは、出力を参照渡しすることで回避できます。

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

class MX {// Matrix 

public:
    MX(double va) {
        elem=va;// only one double for the moment to test the syntaxe
    }
    MX & operator = (const MX &src)
    {   elem = src.elem;
        return *this;
    }
    friend ostream &operator<<(ostream &stream, MX &a);

    double elem;

};

ostream &operator<<(ostream &stream, MX &a)
{
  stream << a.elem ;

  return stream;
}

typedef vector<MX *> MXR; // save pointers only 

class ArgCopyList { // copy the objects to a list
public :
    vector<const MX> data; 
    ArgCopyList(MX &a)
    {
        data.push_back(a);
    };
    ArgCopyList(MX &a, MX &b)
    {
        data.push_back(a);
        data.push_back(b);
    };
    ArgCopyList(MX &a, MX &b, MX &c)
    {
        data.push_back(a);
        data.push_back(b);
        data.push_back(c);
    };
    // do the same for bigger lists

};

class ArgList { // Proxy
public:

    ArgList() {};

    ArgList(const ArgList& src) 
    {
        data.clear();
        for (int i=0 ;i <src.data.size();i++)
            data.push_back(src.data[i]);
    }
    ArgList& operator , ( MX &a){
        data.push_back(&a);
        return *this;
   }

    ArgList  &operator= ( ArgList &src)
    {
        if (this == &src)
            return *this;
        data.clear();
        int n= src.data.size();
        for (int i=0 ;i <n;i++)
            data.push_back(src.data[i]);
        return *this;
    };


   ArgList& operator =( ArgCopyList& src){
        for (int i=0 ;i <data.size();i++)// TBD : must control the size of src & this->data here
            data.at(i)->elem = (src.data[i].elem);
        return *this;
   };
    MXR data; 
};


ArgList operator , (MX& a,  MX& b){
    ArgList out;    
    out.data.push_back(&a);
    out.data.push_back(&b);
    return out;
   }

// test function
ArgCopyList ff(ArgList argins)
{

    int n = argins.data.size();
    MX a= *(argins.data[0]);
    MX b= *(argins.data[1]);
    MX x(a.elem+1.0);
    MX y(b.elem+10.0);
    MX z(a.elem+b.elem);
    return ArgCopyList( x, y , z);

}


int _tmain(int argc, _TCHAR* argv[])
{

    MX a(1.0);MX b(2.0);MX c(3.0);
    cout << "a=" << a << ",b=" << b << ",c=" << c << endl;

    MX x(0.0);MX y(0.0);MX z(0.0);
    cout << "Argouts before calling (x,y,z)= ff((a,b,c)).\nx=" << x << ",y=" << y << ",z=" << z << endl;

    (x,y,z)= ff((a , b, c) );

    cout << "Argouts after calling ff.\nx=" << x << ",y=" << y << ",z=" << z << endl;
    return 0;
}

/* output
a=1,b=2,c=3
Argouts before calling (x,y,z)= ff((a,b,c)).
x=0,y=0,z=0
Argouts after calling ff.
x=2,y=12,z=3
*/
于 2012-04-25T11:51:03.953 に答える