C++ C++;运算符重载与隐式转换
我有一个类封装了一些算术,比如说定点计算。我喜欢重载算术运算符的想法,因此我写了以下内容:C++ C++;运算符重载与隐式转换,c++,operator-overloading,implicit-conversion,C++,Operator Overloading,Implicit Conversion,我有一个类封装了一些算术,比如说定点计算。我喜欢重载算术运算符的想法,因此我写了以下内容: class CFixed { CFixed( int ); CFixed( float ); }; CFixed operator* ( const CFixed& a, const CFixed& b ) { ... } 一切正常。我可以写3*CFixed(0)和CFixed(3)*10.0f。但现在我意识到,我可以用整数操作数更有效地实现运算符*。所以我把它超载了:
class CFixed
{
CFixed( int );
CFixed( float );
};
CFixed operator* ( const CFixed& a, const CFixed& b )
{ ... }
一切正常。我可以写3*CFixed(0)和CFixed(3)*10.0f。但现在我意识到,我可以用整数操作数更有效地实现运算符*。所以我把它超载了:
CFixed operator* ( const CFixed& a, int b )
{ ... }
CFixed operator* ( int a, const CFixed& b )
{ ... }
它仍然有效,但现在CFixed(0)*10.0f调用重载版本,将float转换为int(我希望它将float转换为CFixed)。当然,我也可以重载浮点版本,但对我来说,这似乎是一个组合式的代码爆炸。有什么解决办法吗(或者我的课程设计错了)?如何告诉编译器仅使用int调用运算符*的重载版本 进行转换怎么样?如果您有可以仅用一个参数调用的构造函数,那么您实际上创建了一个隐式转换运算符。在您的示例中,只要需要
CFixed
,就可以传递int
和float
。这当然是危险的,因为编译器可能会悄悄地生成调用错误函数的代码,而不是在您忘记包含某个函数的声明时对您咆哮
因此,一个很好的经验法则是,当您编写只需一个参数即可调用的构造函数时(请注意,这一个foo(inti,bool b=false)
也可以用一个参数调用,即使它需要两个参数),您应该使该构造函数显式,除非你真的想要隐式转换<编译器不使用代码>显式
构造函数进行隐式转换
您必须将您的类更改为:
class CFixed
{
explicit CFixed( int );
explicit CFixed( float );
};
我发现这条规则很少有例外。(std::string::string(const char*)
是一个相当著名的例子。)
编辑:很抱歉,我忽略了不允许从int
到float
的隐式转换的要点
我认为防止这种情况发生的唯一方法是为
float
提供操作符 同意sbi,您应该明确地将单参数构造函数显式化
但是,可以避免使用模板编写的运算符函数爆炸:
template <class T>
CFixed operator* ( const CFixed& a, T b )
{ ... }
template <class T>
CFixed operator* ( T a, const CFixed& b )
{ ... }
模板
固定运算符*(常数固定&a,T b)
{ ... }
模板
固定运算符*(T a、常数固定和b)
{ ... }
根据函数中的代码,这将只使用支持从中转换的类型进行编译。您也应该使用
float
类型重载。从int
转换为用户指定类型(CFixed
)的优先级低于内置的浮点积分转换为float
。因此,编译器将始终选择具有int
的函数,除非您还添加了具有float
的函数
有关更多详细信息,请阅读C++03标准的13.3部分。感受痛苦
看来我也不知道了-(报告称,仅添加float并不能解决问题,因为还应添加带有
double
的版本。但在任何情况下,添加几个与内置类型相关的运算符都是乏味的,但不会导致组合增强。假设您希望为任何整数类型选择专用版本(特别是,不仅仅是int,如果操作数不是整数类型,可以将其作为模板函数提供,并使用Boost.EnableIf从可用重载集中删除这些重载
#include <cstdio>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>
class CFixed
{
public:
CFixed( int ) {}
CFixed( float ) {}
};
CFixed operator* ( const CFixed& a, const CFixed& )
{ puts("General CFixed * CFixed"); return a; }
template <class T>
typename boost::enable_if<boost::is_integral<T>, CFixed>::type operator* ( const CFixed& a, T )
{ puts("CFixed * [integer type]"); return a; }
template <class T>
typename boost::enable_if<boost::is_integral<T>, CFixed>::type operator* ( T , const CFixed& b )
{ puts("[integer type] * CFixed"); return b; }
int main()
{
CFixed(0) * 10.0f;
5 * CFixed(20.4f);
3.2f * CFixed(10);
CFixed(1) * 100u;
}
#包括
#包括
#包括
类固定
{
公众:
CFixed(int){}
固定(浮动){}
};
CFixed运算符*(常数CFixed&a,常数CFixed&)
{puts(“通用CFixed*CFixed”);返回a;}
模板
typename boost::enable_if::type操作符*(const CFixed&a,T)
{puts(“CFixed*[整型]”);返回a;}
模板
typename boost::enable_if::type操作符*(T、const CFixed&b)
{put(“[integer type]*CFixed”);返回b;}
int main()
{
固定(0)*10.0f;
5*C固定(20.4f);
3.2f*CFixed(10);
固定(1)*100u;
}
当然,您也可以使用不同的条件使这些重载仅在T=int:typename boost::enable\u if::type…
至于设计类,也许你可以更多地依赖模板。例如,构造函数可以是一个模板,同样,如果你需要区分整型和实型,应该可以使用这种技术。这有什么帮助?海报需要CFixed(float)如果将浮点传递给运算符*,则调用该函数-添加显式关键字将降低这种可能性。模板内部会发生什么情况?:-)Ẹ同样的事情!@Pavel:事实上,我认为Bill是对的(即使他可能没有意识到这一点))。看看这个:
模板固定操作符*(const-CFixed&a,tb){返回a*CFixed(b);}
这不是OP想要的吗?这是一个坏主意,因为这样的模板会被认为是对操作符*
的任何调用的重载,其中一个参数的类型是CFixed
或可转换为它,即使t
实际上不是*
withCFixed
可用的类型标签-它仍然可以选择该重载,然后在尝试实例化它时失败。由于错误将在操作符*
的主体内,SFINAE不会启动。@sbi:它做OP想要的事情,但它也创建了一个固定的
临时,这是他想要避免的。他最初是从一个操作符开始的*(CFixed,CFixed)
,实际上已经完成了他想要的一切;但是如果他事先知道它是int
,他可以更有效地实现操作符*(CFixed,int)
,这就是他想要se的原因