【问题标题】:Managing Implicit Type Conversion in C++在 C++ 中管理隐式类型转换
【发布时间】:2010-11-06 06:29:22
【问题描述】:

我正在编写执行最近邻查询的代码。用户在搜索中查询数据的方式有两个简单的想法:

  • 离空间中给定点最近的 N 个点。
  • 给定距离内的所有点。

在我的代码中,Points 被放入一个 PointList 中,PointList 是一个容器,负责跟踪在搜索中找到的点。

现在我的 PointList 对象有一个构造函数:

PointList( unsigned int maxvals ); // #1

接下来我要添加的两个构造函数是:

PointList( float maxdist ); // #2
PointList( unsigned int maxvals, float maxdist ); // #3

我的问题是:如何确保我的用户和 C++ 编译器生成正确的 PointList 的构造函数并区分构造函数 1 和 2? 我应该只实现#3 并提供定义任意大的常量吗 maxvals 和 maxdist 的值?另一种选择可能是编写另一个轻量级对象系统来控制将点添加到列表中的逻辑,但对于这样一个简单的想法来说,这感觉有点过头了。

我真的在努力让我的用户明白这一点,他们大多是科学家,他们有时在没有受过正规教育的情况下学习了 C++。谢谢!

【问题讨论】:

    标签: c++ c type-conversion


    【解决方案1】:

    我肯定会使用 explicit 构造函数。在示例中,无符号整数不会隐式转换。

    class A
    {
    public:
        explicit A(float f){}
        explicit A(int i){}
    };
    
    void test(){
        unsigned int uinteger(0);
        A a1(uinteger);        //Fails, does not allow implicit conversions
    
        A a2((float)uinteger); //OK, explicit conversion
    
        float f(0.0);
        A a3(f);               //OK
    
        int integer(0);
        A a4(integer);         //OK
    }
    

    错误信息很容易理解:

    : error C2668: 'A::A' : ambiguous call to overloaded function
    : could be 'A::A(int)'
    : or       'A::A(float)'
    

    【讨论】:

      【解决方案2】:

      考虑使用true typedefs。这对您的客户端代码来说需要更多的努力,但可以保证您的正确性。

      【讨论】:

        【解决方案3】:

        整数类型的重载解决发生在两个类别上,可以非常粗略地总结为

        • Promotion:这是从小于int 的类型到int 或unsigned int 的转换,具体取决于int 是否可以存储源类型的所有值。
        • 转换:这是从任何整数类型到另一种整数类型的转换。

        类似地,浮点类型的转换发生在两个类别上

        • 促销:这是从float 到double 的转换
        • 转换:这是从任何浮点类型到另一种浮点类型的转换

        还有从整数到浮点数或反向的转换。这被列为转化,而不是促销。促销比转化排名更好,并且在只需要促销的情况下,这种情况将是首选。因此,您可以使用以下构造函数

        PointList( int maxVals );
        PointList( unsigned int maxVals );
        PointList( long maxVals );
        PointList( unsigned long maxVals );
        
        PointList( double maxDist );
        PointList( long double maxDist );
        

        对于任何整数类型,这应该选择第一组构造函数。对于任何浮点类型,这应该选择第二组构造函数。例如,如果您传递了int,您原来的两个构造函数很容易导致float 和unsigned int 之间的歧义。对于另外两个参数构造函数,您可以根据需要使用您的解决方案。


        也就是说,我也会使用工厂函数,因为我认为决定参数含义的类型非常脆弱。大多数人会期望以下结果等于

        PointList p(floor(1.5));
        PointList u((int)1.5);
        

        但它会导致不同的事态。

        【讨论】:

          【解决方案4】:

          这要求好好阅读Overload Resolution。

          【讨论】:

            【解决方案5】:

            如果构造函数 #1 和 #2 存在,并且您插入的值是 float 或 int 且不应发生转换,则将调用正确的构造函数。因此,只需确保您使用显式调用的数字类型(即 1f 和 1)。 构造函数#3 似乎不是一个很好的选择,因为它并不是真正必要的,只会让代码的用户感到困惑。 如果您需要任一数字的默认值,您可以使用

            PointList(int max, float max=VALUE)
            

            和

            PointList(float max, int max=VALUE)
            

            再次重申:在代码可读性方面,这似乎比代码造成的危害更大。

            【讨论】:

              【解决方案6】:

              为什么不使用工厂方法而不是构造函数?工厂方法具有可自定义名称的优势。

              
              static PointList createNearestValues(unsigned int maxvals) {}
              static PointList createByDistance(float maxdist) {}
              

              【讨论】:

                【解决方案7】:

                第一个调用 PointList(10),第二个调用 PointList(10f)。

                第二种,你也可以使用10.0。

                【讨论】:

                • 这绝对是一种可能性,但我的用户可能不理解其中的区别。其中一些可能对编译器区分浮点数和无符号整数的方式并不复杂。
                猜你喜欢
                • 1970-01-01
                • 2011-12-25
                • 1970-01-01
                • 2011-07-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多