C ++运算符重载和隐式转换

时间:2009-10-22 14:34:22

标签: 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。但是现在我意识到,我可以用整数操作数实现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 *的重载版本?

5 个答案:

答案 0 :(得分:4)

您也应该使用float类型重载。从int到用户指定类型(CFixed)的转换优先级低于内置浮点积分转换为float的优先级。因此编译器将始终选择int函数,除非您添加float函数。

有关更多详细信息,请阅读C ++ 03标准的13.3部分。感受痛苦。

似乎我也忘记了它。 :-(UncleBens报告说添加float只能解决问题,因为还应添加带double的版本。但无论如何添加几个与内置类型相关的运算符是单调乏味的,但不会导致组合增强。

答案 1 :(得分:3)

如果你有只能使用一个参数调用的构造函数,那么你有效地创建了一个隐式转换运算符。在您的示例中,只要需要CFixed,就可以传递intfloat。这当然是危险的,因为编译器可能会默默地生成调用错误函数的代码,而不是在您忘记包含某些函数声明时咆哮。

因此,一个好的经验法则说,每当你编写只能用一个参数调用的构造函数时(请注意,这个foo(int i, bool b = false)也可以用一个参数调用,即使它需要两个参数参数),你应该创建那个构造函数explicit,除非你真的想要隐式转换。explicit构造函数不被编译器用于隐式转换。

您必须将课程更改为:

class CFixed
{
   explicit CFixed( int   );
   explicit CFixed( float );
};

我发现这条规则很少有例外。 (std::string::string(const char*)是一个相当着名的。)

编辑:对不起,我错过了关于不允许从intfloat进行隐式转换的观点。

我认为防止这种情况的唯一方法是为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 ) 
{ ... } 

根据函数中的代码,这将仅使用您支持的转换类型进行编译。