7

我有一个封装了一些算术的类,比如说定点计算。我喜欢重载算术运算符的想法,所以我写了以下内容:

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 )。当然,我也可以重载浮点版本,但这对我来说似乎是代码的组合爆炸。是否有任何解决方法(或者我的课程设计错误)?如何告诉编译器仅使用整数调用重载版本的 operator*?

4

5 回答 5

4

您也应该重载float类型。从int到用户指定类型 ( CFixed) 的转换的优先级低于到 的内置浮点整数转换float。所以编译器总是会选择 function with int,除非你也添加 function with float

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

好像我也忘记了。:-( UncleBens报告说只添加 float 并不能解决问题,因为版本 withdouble也应该添加。但无论如何添加几个与内置类型相关的运算符是乏味的,但不会导致组合提升.

于 2009-10-22T14:45:33.263 回答
3

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

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

您必须将您的课程更改为:

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

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

编辑:对不起,我错过了不允许从intto隐式转换的要点float

我认为防止这种情况的唯一方法是同时提供操作员float

于 2009-10-22T14:42:28.417 回答
2

假设您希望为任何整数类型选择专用版本(而不仅仅是特别是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 ...

至于设计类,也许你可以更多地依赖模板。例如,构造函数可以是一个模板,同样,如果您需要区分整数类型和实数类型,应该可以使用这种技术。

于 2009-10-22T16:18:19.113 回答
0

如何使转换显式

于 2009-10-22T14:41:30.027 回答
0

同意 sbi,你绝对应该让你的单参数构造函数显式。

您可以避免使用模板编写的 operator<> 函数的爆炸式增长,但是:

template <class T>
CFixed operator* ( const CFixed& a, T b ) 
{ ... } 

template <class T>
CFixed operator* ( T a, const CFixed& b ) 
{ ... } 

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

于 2009-10-22T14:43:00.623 回答