操作符重载问题,哪个编译器是正确的?
今天写一个矩阵运算的程序,为了方便,对操作符进行了重载。
写的时候是在VC6下写的,完了又在Dev下编译了一下,发现Dev下竟然不支持连乘。
即不支持 a*b*c
而VC下却是可以编译通过的
到底哪个编译器是正确的?
typedef struct _TMatrix {
double row[3][3];
} TMatrix;
TMatrix operator *(TMatrix &T1,TMatrix &T2)
{
TMatrix T;
int i,j,k;
for (i=0;i <3;i++)
for (j=0;j <3;j++)
for(k=0;k <3;k++)
T.row[i][j]+=T1.row[i][k]*T2.row[k][j];
return T;
}
[解决办法]
函数参数用const TMatrix &T1这样
[解决办法]
可以连乘才对. 考虑一下内置类型处理方式, 自定义的类应该模仿它们.
lovewhzlq 的方案不太好. 无论如何返回引用一定是错了.
应该这样吧.
inline const TMatrix operator*(TMatrix & const lhs,TMatrix & const rhs);
参考一下. EC++ 3 , item 21
[解决办法]
临时变量只能做右值,这个是标准的规定……
老实说……这个相当晦涩呢,C++。
TMatrix& t = TMatrix();这个显然也不能通过的。
[解决办法]
一、函数按值返回的是临时变量,不是局部栈。
二、临时变量可以绑定在cosnt reference。
[解决办法]
三、参考 2003-8.5.3
A reference to type “cv1T1” is initialized by an expression of type “cv2T2” as follows:
— If the initializer expression
—— is an lvalue (but is not a bit-field), and “cv1T1” is reference-compatible with “cv2T2,” or
—— has a class type (i.e., T2 is a class type) and can be implicitly converted to an lvalue of type
— Otherwise, the reference shall be to a non-volatile const type (i.e.,cv1shall be const).
不好粘,后面的略了,反正都是const。
简单地说,临时对象只能被 non-volatile const 引用。
[解决办法]
typedef struct _TMatrix {
double row[3][3];
} TMatrix;
TMatrix& operator *(TMatrix T1,TMatrix T2)
{
int i,j,k;
TMatrix *T=new TMatrix;//因为这是堆内存,一般由程序员分配释放,回
for (i=0;i <3;i++)
for (j=0;j <3;j++)
T-> row[i][j]=0;
for (i=0;i <3;i++)
for (j=0;j <3;j++)
for(k=0;k <3;k++)
T-> row[i][j]+=T1.row[i][k]*T2.row[k][j];
return *T;
delete T;//释放内存
}
//这个函数应该没什么问题了,大家看看!!
void main()
{
TMatrix a={2,2,2,2,2,2,2,2,2};
TMatrix b={1,1,1,1,1,1,1,1,1};
TMatrix c={1,1,1,1,1,1,1,1,1};
TMatrix d;
d=(a*b*c);
for (int i=0;i <3;i++)
for (int j=0;j <3;j++)
cout < <d.row[i][j] < < " ";
cout < <endl;
}
[解决办法]
这个确实比较难理解。 iambic() 贴的标准我也看到了。我的疑问是:
TMatrix get_martix()
{
return TMatrix();
}
int main()
{
get_martix() = TMatrix();
}
这样的程序,即使在g++中也是正确的。get_martix可以放在等式的左边(这里是赋值而不是初始化吧),说明get_martix() yeild 一个 lvalue 。那么是否应该符合第一条呢?
—— is an lvalue (but is not a bit-field), and “cv1T1” is reference-compatible with “cv2T2,”
TMatrix(cv1T1)显然是 reference-compatible with TMartix(cv2T2) 的。而TMatrix operator *(TMatrix &T1,TMatrix &T2) 是 an expresion of Type cv2T2 。这样看来,TMatrix& (a reference to cv1T1)是可以用 TMatrix operator *(TMatrix &T1,TMatrix &T2) 的结果初试化的。
标准中后面举的例子都是内建类型的,所以对类类型该怎样理解,比较不放心。虽然我发现在标准中,所有出现临时对象的地方,都赋给了const reference。但是要找到根据还是比较困难的。
但是别急,标准13.3.3.1.4 还有这一句:
A standard conversion sequence cannot be formed if it requires binding a reference to non-const to an rvalue (except when binding an implicit object parameter; see the special rules for that case in 13.3.1). [Note: this means, for example, that a candidate function cannot be a viable function if it has a non-const reference parameter (other than the implicit object parameter) and the corresponding argument is a temporary or would require one to be created to initialize the reference (see 8.5.3). ]
它的正文和Note似乎可以推论出 a temporary 是 an rvalue ;从而 a temporary 不可以赋给a non-const reference parameter 。那么,也许我开始举的例子是一个特例:或者get_martix() = TMatrix(); 是特殊的,或者函数返回的类类型临时对象不受13.3.3.1.4的约束。
该如何看待一个函数的返回值(类类型的临时对象)赋给另一个函数的参数(该对象类型的 non-const reference),确实是一个难题,期待更多的讨论。
但不管怎么说,将 TMatrix operator *(TMatrix &T1,TMatrix &T2)
改成 TMatrix operator *(const TMatrix &T1,const TMatrix &T2)
总是可以的。
也许一些难题之所以成为难题,是因为没有必要去解决它,直接避免它就可以了。
P.S. 标准比较难办的地方是:即使有一个相对通用的条款,只要在其他地方还有一条特殊的(more specified )条款,那么在后者规定的条件下,特殊条款优先通用条款。所以,当看到一个条款时,并不能确定它在所有条件下都成立,还要看其他地方有没有更特殊条款(郁闷!);何况它许多地方都很晦涩的。
[解决办法]
是我前面搞错了,我贴的那部分不足以成为证明。(不过结论应该是对的。)
函数的返回值是一个临时对象,临时对象“应该”是一个rvalue,但是函数返回值可以是rvalue。
我记得《Efective C++》中提到过这个不一致的问题,刚才翻了下没找到……
但是如:
TMatrix operator *(const TMatrix &T1,const TMatrix &T2)
还是应该尽量写成
const TMatrix operator *(const TMatrix &T1,const TMatrix &T2)
以避免
(a * b) = c;
这样的用法。
[解决办法]
右值和const不是一个概念。右值是不能被绑定到const指针和引用上,并非右值就是const。
TMatrix get_martix()
{
return TMatrix();
}
int main()
{
get_martix() = TMatrix();
}
这种写法之所以被支持,是为了类似于这样的做法:
list <int> ListOne;
//.....
ListOne(list <int> (newsize).swap());
这种用法其实也不算少见。
楼上的写法,不合适,返回值不应当写为const变量,为何?
因为标准支持这样做:(a*b).func(),如果返回const变量,这里就只能调用const函数。
[解决办法]
编译器不会错,错的只可能是写程序的人!
不要关心编译器是否符合标准,选择了一种编译器就要去适应它!