我有一个封装了一些算术的类,比方说定点计算。我喜欢重载算术运算符的想法,所以我写了以下内容:
class CFixed
{
CFixed( int );
CFixed( float );
};
CFixed operator* ( const CFixed& a, const CFixed& b )
{ ... }
一切正常。我可以写3 * CFixed(0)和CFixed(3)* 10.0f。但是现在我意识到,我可以用整数操作数实现operator *更加有效。所以我重载它:
CFixed operator* ( const CFixed& a, int b )
{ ... }
CFixed operator* ( int a, const CFixed& b )
{ ... }
它仍然有效,但现在CFixed(0)* 10.0f调用重载版本,将float转换为int(我希望它将float转换为CFixed)。当然,我也可以重载浮点版本,但它似乎是我的代码组合爆炸。有没有解决方法(或者我设计我的课程错了)?如何告诉编译器只使用int调用operator *的重载版本?
答案 0 :(得分:4)
您也应该使用float
类型重载。从int
到用户指定类型(CFixed
)的转换优先级低于内置浮点积分转换为float
的优先级。因此编译器将始终选择int
函数,除非您添加float
函数。
有关更多详细信息,请阅读C ++ 03标准的13.3部分。感受痛苦。
似乎我也忘记了它。 :-(UncleBens报告说添加float只能解决问题,因为还应添加带double
的版本。但无论如何添加几个与内置类型相关的运算符是单调乏味的,但不会导致组合增强。
答案 1 :(得分:3)
如果你有只能使用一个参数调用的构造函数,那么你有效地创建了一个隐式转换运算符。在您的示例中,只要需要CFixed
,就可以传递int
和float
。这当然是危险的,因为编译器可能会默默地生成调用错误函数的代码,而不是在您忘记包含某些函数声明时咆哮。
因此,一个好的经验法则说,每当你编写只能用一个参数调用的构造函数时(请注意,这个foo(int i, bool b = false)
也可以用一个参数调用,即使它需要两个参数参数),你应该创建那个构造函数explicit
,除非你真的想要隐式转换。explicit
构造函数不被编译器用于隐式转换。
您必须将课程更改为:
class CFixed
{
explicit CFixed( int );
explicit CFixed( float );
};
我发现这条规则很少有例外。 (std::string::string(const char*)
是一个相当着名的。)
编辑:对不起,我错过了关于不允许从int
到float
进行隐式转换的观点。
我认为防止这种情况的唯一方法是为float
提供运算符。
答案 2 :(得分:1)
假设您希望为任何整数类型挑选专用版本(特别是 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;
}
当然,您也可以使用不同的条件来使这些重载仅在T = int时可用:typename boost::enable_if<boost::is_same<T, int>, CFixed>::type ...
关于设计课程,也许你可以更多地依赖模板。例如,构造函数可以是模板,如果需要区分整数类型和实数类型,应该可以使用这种技术。
答案 3 :(得分:0)
如何进行转化explicit?
答案 4 :(得分:0)
同意sbi,你一定要明确你的单参数构造函数。
您可以避免运营商爆炸&lt;&gt;但是,您使用模板编写的函数:
template <class T>
CFixed operator* ( const CFixed& a, T b )
{ ... }
template <class T>
CFixed operator* ( T a, const CFixed& b )
{ ... }
根据函数中的代码,这将仅使用您支持的转换类型进行编译。