简体   繁体   English

C++ 逗号运算符重载和引用向量

[英]C++ comma operator overloading and vector of references

There are many posts (questions) about comma operator overloading in c++.有很多关于 C++ 中逗号运算符重载的帖子(问题)。 Most of the answers advice to DO NOT use comma operator overloading.大多数答案建议不要使用逗号运算符重载。 I want to write a c++ library with a syntax very similar to Matlab language.我想编写一个语法与 Matlab 语言非常相似的 c++ 库。 The basic object is a Matrix MX.基本对象是 Matrix MX。 I want to be able to make the end-user of the library to write expressions like :我希望能够让库的最终用户编写如下表达式:

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

I have an idea about how to do make the setter & the getter work as written above using a Proxy Class that save a pointer to the MX object and save also the indices i,j objects.我有一个想法,关于如何使用保存指向 MX 对象的指针并保存索引 i,j 对象的代理类来使 setter 和 getter 像上面所写的那样工作。 For example b(i,j) will create a Proxy object ProxMX(b,i,j).例如 b(i,j) 将创建一个代理对象 ProxMX(b,i,j)。 Then we define a method to assign ProxMX to MX and visversa (using operator =) that do the hard job of getting & setting the elements of b.然后我们定义一个方法将 ProxMX 分配给 MX 和 visversa(使用运算符 =),它们完成了获取和设置 b 的元素的艰巨工作。

I need help to make function calls like :我需要帮助来进行函数调用,例如:

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

Where a, b, c are inputs argments (MX objects) and x, y , z are output argments .其中 a、b、c 是输入参数(MX 对象),x、y、z 是输出参数。 if the above syntaxe is not not possible i can think about a syntaxe like :如果上述语法是不可能的,我可以考虑这样的语法:

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

I started writing this test code:我开始编写这个测试代码:

#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;
}

This code compiles & runs without errors using VS2010 Express :).此代码使用 VS2010 Express 编译和运行没有错误:)。 But as expected it does not give the expected results because I need to save references to variables in the ArgList instead of making a copy of objects to vector.但正如预期的那样,它没有给出预期的结果,因为我需要在 ArgList 中保存对变量的引用,而不是将对象复制到向量中。 I know that we cannot use std::vector as a container of references to objects.我知道我们不能使用 std::vector 作为对象引用的容器。

Any help in order to make theses expressions writables and working in c++ code.为了使这些表达式可写并在 C++ 代码中工作的任何帮助。 Thanks.谢谢。

I'm using C++11 to outline you a solution (the code is tested, too), but this is doable in C++03 (using eg Boost.Tuple):我正在使用 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));
}

Usage looks like (assuming this operator resides in namespace ns ):用法如下所示(假设此运算符位于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);

Here are the attached caveats:以下是附带的注意事项:

  • as you've seen, you need a using declaration to bring operator, in scope.如您所见,您需要一个 using 声明来将operator,纳入范围。 Even if the types X , Y , Z reside in the same scope of operator, (to enable ADL), std::tuple doesn't.即使类型XYZ位于相同的operator,范围内(以启用 ADL), std::tuple也不会。 You could do something like template<typename... T> struct tuple: std::tuple<T...> { using std::tuple<T...>::tuple; };可以做类似template<typename... T> struct tuple: std::tuple<T...> { using std::tuple<T...>::tuple; }; template<typename... T> struct tuple: std::tuple<T...> { using std::tuple<T...>::tuple; }; (not as convenient to do in C++03 however) to have your own tuple in the appropriate namespace to have ADL in a quick-and-dirty way. (但是在 C++03 中做起来不方便)在适当的命名空间中拥有自己的元组,以便以快速而肮脏的方式拥有 ADL。 However:然而:

  • overloaded operators must always operate on at least one user-defined type.重载运算符必须始终对至少一种用户定义类型进行操作。 So if types X and Y both happen to be types like int or double , then you get the default operator, .因此,如果类型XY都恰好是intdouble之类的类型,那么您将获得默认operator, . The usual solution to things like this is to require the client to do instead something like (ref(x), ref(y), z) = ff(a, b, c);此类事情的通常解决方案是要求客户端执行类似(ref(x), ref(y), z) = ff(a, b, c);之类的操作。 where ref will return a type in the appropriate namespace (for ADL purposes, again).其中ref将返回适当命名空间中的类型(再次出于 ADL 目的)。 Perhaps such type can be implemented in terms of std::reference_wrapper (or the Boost version, for C++03) with the same quick-and-dirty hack as for the tuple.也许这种类型可以用std::reference_wrapper (或 C++03 的 Boost 版本)来实现,使用与元组相同的快速和肮脏的 hack。 (You'd need additional overloads of operator, .) (您需要额外的operator, 。)

All in all, that's a lot of work (with ugly workarounds) when something like总而言之,当类似

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

or perhaps也许

/* 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);

works out of the box (even in C++03 with Boost.Tuple).开箱即用(即使在带有 Boost.Tuple 的 C++03 中)。 My advice in this matter would be (with no slight intended): Keep it simple, stupid!我在这件事上的建议是(无意):保持简单,愚蠢! and to use that.并使用它。

In C++11, you can do this using tuples:在 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);

If your compiler doesn't support that, the Boost.Tuple library is very similar, but may be more restricted without support from variadic templates.如果您的编译器不支持, Boost.Tuple库非常相似,但在没有可变参数模板支持的情况下可能会受到更多限制。

If you really want to support syntax like (x,y,z) = ff(a,b,c);如果你真的想支持像(x,y,z) = ff(a,b,c);这样的语法, which might be rather confusing to a C++ programmer even if it does look sensible in Matlab, then you're almost there. ,这对 C++ 程序员来说可能相当混乱,即使它在 Matlab 中看起来确实很合理,那么你就快到了。 You need a separate type similar to your ArgList (perhaps called something like RefList ), which contains pointers rather than values, initialises these pointers from non- const references to the results, and has an assignment operator that takes an ArgList (or some other collection of values) and assigns each element via the pointers.您需要一个类似于ArgList的单独类型(可能称为RefList类的东西),它包含指针而不是值,从对结果的非const引用初始化这些指针,并有一个采用ArgList (或其他一些集合)的赋值运算符值)并通过指针分配每个元素。

You might also want to look atBoost.Assignment to see how that kind of operator overloading can be made to work.您可能还想查看Boost.Assignment以了解如何使这种运算符重载工作。 It's a bit of a hassle;这有点麻烦; in particular, you can't overload operators that act only on built-in types, which rather limits the usefulness of that approach.特别是,您不能重载仅作用于内置类型的运算符,这反而会限制该方法的实用性。 Personally, I'd use variadic templates instead if C++11 is an option.就个人而言,如果 C++11 是一个选项,我会使用可变参数模板。

Thank you all for your sugesstions & feedback.谢谢大家的建议和反馈。 here is a working example using 2 proxies.这是一个使用 2 个代理的工作示例。 ArgList contains pointers to MX objects and ArgCopyList contains copies of MX objects. ArgList 包含指向 MX 对象的指针,而 ArgCopyList 包含 MX 对象的副本。

From a matlab user point of view the c++ syntaxe (x,y,...)=myfunc((a,b,...)) is very similar to the matlab expression [x,y,...]=myfunc(a,b,...) .从 matlab 用户的角度来看,c++ 语法(x,y,...)=myfunc((a,b,...))与 matlab 表达式[x,y,...]=myfunc(a,b,...)非常相似[x,y,...]=myfunc(a,b,...) But my first impression is that this kind of function calling is not efficient at all because the returned values are copied which could be avoided by passing outputs by reference.但我的第一印象是,这种函数调用根本没有效率,因为返回的值被复制了,这可以通过引用传递输出来避免。

#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
*/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM