我有一个类封装了一些算术,比如说定点计算。我喜欢重载算术运算符的想法,所以我写了以下内容:
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 )。当然,我也可以重载浮点版本,但这对我来说似乎是代码的组合爆炸。有没有解决方法(或者我设计的类是错误的)?如何告诉编译器仅使用整数调用重载版本的运算符*?
I have a class that encapsulates some arithmetic, let's say fixed point calculations. I like the idea of overloading arithmetic operators, so I write the following:
class CFixed
{
CFixed( int );
CFixed( float );
};
CFixed operator* ( const CFixed& a, const CFixed& b )
{ ... }
It all works. I can write 3 * CFixed(0) and CFixed(3) * 10.0f. But now I realize, I can implement operator* with an integer operand much more effective. So I overload it:
CFixed operator* ( const CFixed& a, int b )
{ ... }
CFixed operator* ( int a, const CFixed& b )
{ ... }
It still works, but now CFixed(0) * 10.0f calls overloaded version, converting float to int ( and I expected it to convert float to CFixed ). Of course, I can overload a float versions as well, but it seems a combinatorial explosion of code for me. Is there any workaround (or am I designing my class wrong)? How can I tell the compiler to call overloaded version of operator* ONLY with ints?
发布评论
评论(5)
您还应该重载
float
类型。从int
到用户指定类型 (CFixed
) 的转换的优先级低于内置浮点积分到float
的转换。因此编译器将始终选择带有int
的函数,除非您也添加带有float
的函数。有关更多详细信息,请阅读 C++03 标准的 13.3 部分。感受疼痛。
看来我也已经失去了踪迹。 :-( UncleBens 报告仅添加 float 并不能解决问题,因为
double
也应该添加,但无论如何,添加多个与内置类型相关的运算符是乏味的,但不会导致组合提升。You should overload with
float
type as well. Conversion fromint
to user-specified type (CFixed
) is of lower priority than built-in floating-integral conversion tofloat
. So the compiler will always choose function withint
, unless you add function withfloat
as well.For more details, read 13.3 section of C++03 standard. Feel the pain.
It seems that I've lost track of it too. :-( UncleBens reports that adding float only doesn't solve the problem, as version with
double
should be added as well. But in any case adding several operators related to built-in types is tedious, but doesn't result in a combinatorial boost.如果您有只能使用一个参数调用的构造函数,那么您实际上创建了一个隐式转换运算符。在您的示例中,只要需要
CFixed
,就可以传递int
和float
。这当然是危险的,因为当您忘记包含某些函数的声明时,编译器可能会默默地生成调用错误函数的代码,而不是对您咆哮。因此,一个好的经验法则是,每当您编写只能使用一个参数调用的构造函数时(请注意,这个
foo(int i, bool b = false)
可以使用一个参数调用参数,即使它需要两个参数),您应该使该构造函数显式,除非您确实希望启动隐式转换。显式构造函数不被使用隐式转换的编译器。您必须将您的课程更改为:
我发现这条规则很少有例外。 (
std::string::string(const char*)
是一个相当著名的。)编辑: 抱歉,我错过了关于不允许隐式转换的要点从
int
到float
。我认为防止这种情况的唯一方法是同时提供
float
运算符。If you have constructors which can be invoked with just one argument, you effectively created an implicit conversion operator. In your example, wherever a
CFixed
is needed, both anint
and afloat
can be passed. This is of course dangerous, because the compiler might silently generate code calling the wrong function instead of barking at you when you forgot to include some function's declaration.Therefore a good rule of thumb says that, whenever you're writing constructors that can be called with just one argument (note that this one
foo(int i, bool b = false)
can be called with one argument, too, even though it takes two arguments), you should make that constructorexplicit
, unless you really want implicit conversion to kick in.explicit
constructors are not used by the compiler for implicit conversions.You would have to change your class to this:
I have found that there are very few exceptions to this rule. (
std::string::string(const char*)
is a rather famous one.)Edit: I'm sorry, I missed the point about not allowing implicit conversions from
int
tofloat
.The only way I see to prevent this is to provide the operators for
float
as well.假设您希望为任何整数类型选择专用版本(而不仅仅是 int 特别是,您可以做的一件事是将其作为模板函数提供并使用 Boost.EnableIf 删除那些可用重载集中的重载,如果操作数不是整数类型,
当然,您也可以使用不同的条件使这些重载仅在 T=int 时才可用:
typename boost::enable_if, CFixed>::type ...
至于设计类,也许您可以更多地依赖模板,例如,构造函数可以是模板,并且您是否需要区分整数。和真实类型,应该可以使用这种技术。
Assuming you'd like the specialized version to be picked for any integral type (and not just int in particular, one thing you could do is provide that as a template function and use Boost.EnableIf to remove those overloads from the available overload set, if the operand is not an integral type.
Naturally, you could also use a different condition to make those overloads available only if T=int:
typename boost::enable_if<boost::is_same<T, int>, CFixed>::type ...
As to designing the class, perhaps you could rely on templates more. E.g, the constructor could be a template, and again, should you need to distinguish between integral and real types, it should be possible to employ this technique.
如何显式转换?
How about making the conversion explicit?
同意 sbi 的观点,你绝对应该明确你的单参数构造函数。
您可以避免操作员<>中的爆炸但是,您使用模板编写的函数:
根据函数中的代码,这只会使用您支持转换的类型进行编译。
Agree with sbi, you should definitely make your single-parameter constructors explicit.
You can avoid an explosion in the operator<> functions you write with templates, however:
Depending on what code is in the functions, this will only compile with types that you support converting from.