代码之家  ›  专栏  ›  技术社区  ›  SadSido

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

  •  7
  • SadSido  · 技术社区  · 15 年前

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

    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)。当然,我也可以重载float版本,但对我来说,这似乎是代码的组合爆炸式增长。是否有任何解决方法(或者我设计的课程有错)?我怎样才能告诉编译器只用ints调用操作符*的重载版本?

    5 回复  |  直到 15 年前
        1
  •  1
  •   UncleBens    15 年前

    假设您希望为任何整型(而不仅仅是 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 ...

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

        2
  •  4
  •   Community CDub    8 年前

    你应该超载 float 类型也一样。从转换 int 到用户指定的类型( CFixed )优先级低于内置的浮点积分转换到 浮动 . 所以编译器总是选择函数 int ,除非添加函数 浮动 也。

    欲了解更多细节,请阅读13.3节的C++ 03标准。感觉疼痛。

    看来我也不知道了。: UncleBens 报告说,仅添加float并不能解决问题,因为版本 double 也应该添加。但是在任何情况下,添加几个与内置类型相关的操作符都是繁琐的,但不会导致组合增强。

        3
  •  3
  •   sbi    9 年前

    如果您有只能用一个参数调用的构造函数,那么您实际上创建了一个隐式转换运算符。在您的示例中,无论 CFixed 两者都是必需的 int 和A float 可以通过。这当然是危险的,因为编译器可能会悄悄地生成调用错误函数的代码,而不是在忘记包含某个函数的声明时冲你狂吠。

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

    你必须将你的课程改为:

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

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

    编辑: 抱歉,我错过了不允许隐式转换的要点 int 浮动 .

    我看到的唯一防止这种情况的方法是为 浮动 也。

        4
  •  0
  •   Ashalynd    15 年前

    转换一下怎么样 explicit ?

        5
  •  0
  •   Bill    15 年前

    同意SBI,您应该明确地将单参数构造函数设置为显式的。

    但是,您可以避免使用模板编写的运算符<gt;函数发生爆炸:

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

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