在c ++中有很多关于逗号运算符重载的帖子(问题)。 大多数答案建议不要使用逗号运算符重载。 我想编写一个c ++库,其语法与Matlab语言非常相似。 基本对象是Matrix MX。我希望能够让库的最终用户编写如下表达式:
MX a = b(i);// get b elements at indices i
b(i,j)= a; // set elements of b at indices i,j.
我知道怎样做制定者& getter工作如上所述使用代理类保存指向MX对象的指针并保存索引i,j对象。例如,b(i,j)将创建代理对象ProxMX(b,i,j)。然后我们定义了一个方法来将ProxMX分配给MX和visversa(使用operator =),这样做可以很好地获得&设置b的元素。
我需要帮助来进行如下函数调用:
(x,y,z)= ff(a,b,c)
其中a,b,c是输入argments(MX对象),x,y,z是输出argments。 如果上面的语法不可能,我可以考虑一下这样的语法:
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;
}
此代码编译&amp;使用VS2010 Express运行没有错误:)。但正如预期的那样,它不会给出预期的结果,因为我需要保存对ArgList中变量的引用,而不是将对象的副本转换为vector。我知道我们不能使用std :: vector作为对象引用的容器。
任何帮助,以使这些表达式可写并使用c ++代码。 感谢。
答案 0 :(得分: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,
放在范围内。即使类型X
,Y
,Z
位于operator,
的同一范围内(启用ADL),std::tuple
也不会。您可以执行类似template<typename... T> struct tuple: std::tuple<T...> { using std::tuple<T...>::tuple; };
的操作(但在C ++ 03中不那么方便)在适当的命名空间中拥有自己的元组,以便以快速而肮脏的方式使用ADL 。但是:
重载运算符必须始终至少对一个用户定义的类型进行操作。因此,如果X
和Y
类型恰好是int
或double
类型,那么您将获得默认的operator,
。对此类事情的通常解决方案是要求客户端执行类似(ref(x), ref(y), z) = ff(a, b, c);
的操作,其中ref
将在相应的命名空间中返回类型(再次用于ADL)。也许这样的类型可以用std::reference_wrapper
(或者Boost版本,对于C ++ 03)实现,具有与元组相同的快速和肮脏的黑客。 (您需要额外的operator,
重载。)
总而言之,当像
这样的事情时,这是很多工作(有丑陋的解决方法)/* 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中)。我在这个问题上的建议是(没有轻微意图):保持简单,愚蠢!并使用它。
答案 1 :(得分: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);
这样的语法,这可能会让C ++程序员感到困惑,即使它在Matlab中看起来很合理,那么你几乎就在那里。你需要一个类似于ArgList
的单独类型(可能称为RefList
),它包含指针而不是值,从非const
引用结果初始化这些指针,并且一个赋值运算符,它接受ArgList
(或其他一些值集合)并通过指针分配每个元素。
您可能还想查看Boost.Assignment,了解如何使这种运算符重载工作。这有点麻烦;特别是,您不能重载仅对内置类型起作用的运算符,这会限制该方法的有用性。就个人而言,如果C ++ 11是一个选项,我会使用可变参数模板。
答案 2 :(得分: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
*/