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

如何处理find函数中的“item not found”情况?

c++
  •  2
  • StackedCrooked  · 技术社区  · 15 年前

    我经常遇到这样的情况:我需要以某种方式报告某个项目的发现失败了。既然有很多方法可以应付这种情况,我总是不确定该怎么做。以下是几个例子:

    class ItemCollection
    {
    public:
    
        // Return size of collection if not found.
        size_t getIndex(Item * inItem)
        {
            size_t idx = 0;
            for (; idx != mItems.size(); ++idx)
            {
                if (inItem == mItems[idx])
                {
                    return idx;
                }
            }
            return idx;
        }
    
    
        // Use signed int and return -1 if not found.
        int getIndexV2(Item * inItem)
        {            
            for (int idx = 0; idx != mItems.size(); ++idx)
            {
                if (inItem == mItems[idx])
                {
                    return idx;
                }
            }
            return -1;
        }
    
        // Throw exception if not found.
        size_t getIndexV3(Item * inItem)
        {
            for (size_t idx = 0; idx != mItems.size(); ++idx)
            {
                if (inItem == mItems[idx])
                {
                    return idx;
                }
            }
            throw std::runtime_error("Item not found");
        }
    
        // Store result in output parameter and return boolean to indicate success. 
        bool getIndex(Item * inItem, size_t & outIndex)
        {
            for (size_t idx = 0; idx != mItems.size(); ++idx)
            {
                if (inItem == mItems[idx])
                {
                    outIndex = idx;
                    return true;
                }
            }
            return false;
        }
    
    
    private:
        std::vector<Item*> mItems; 
    };
    

    在我(年轻的)编程Carreer中,我在某个时候使用了所有这些。我主要使用“返回集合大小”方法,因为它类似于STL迭代器的工作方式。不过,我想在未来做出更有教育意义的选择。那么,关于如何处理未发现的错误的决策应该基于什么设计原则呢?

    8 回复  |  直到 15 年前
        1
  •  4
  •   Rob Kennedy    15 年前

    你的功能更像 std::string::find 比算法头中任何基于迭代器的函数都要多。它返回一个 指数 不是迭代器。

    我不喜欢您的函数返回集合大小来模拟“一个超过结尾”。它要求调用者知道集合大小以检查函数是否成功。我更喜欢您的第二个函数,因为它返回一个常数值,这意味着“找不到”。 std::string 类型通过返回组合这两种类型 std::string::npos ,其值为-1,但作为 未签名的 类型。

    请远离第三个函数的异常方法,除非您有其他调用的函数提前告诉您该项 被发现。也就是说,为调用者提供一些避免异常的方法。

    当返回的索引即使在项 不是 找到了。如果您正在进行二进制搜索,那么了解项目所在的索引可能会很有用。 如果它在集合中就可以找到。然后你可以提供一个 insert 接受该值作为提示的函数,就像 std::map::insert . 如果您不能提供这种信息,那么就不要使用这种函数,因为调用方使用它会比较麻烦。你最好选择你的第一种风格。

        2
  •  4
  •   Loki Astari    15 年前

    像STL一样。

    在大多数情况下,这意味着返回一个超过结尾的迭代器。
    所以它可以与end()进行比较。

    您的情况更像是std::string,在这里您将与常量值进行比较。
    我会修改你的-1案例(因为神奇的数字往往是脆弱的)。创建类的静态常量成员(不要在文档中定义该值,只说它只能在查找失败时返回),并针对该常量进行测试。然后,对于类的版本1,将这个常量设为-1。

    class ItemCollection
    {
        static int const npos = -1;
    
        .....
    
        int getIndexV2(Item * inItem)  // returns npos on failure.
    
    };
    
        3
  •  2
  •   rmn    15 年前

    使用Boost.Optional之类的工具。

    阅读它背后的想法,以及如何自己实现它,这里: http://cplusplus.co.il/2009/12/04/boost-optional-and-its-internals/

    它还包含一个与您的确切问题相匹配的示例。

        4
  •  1
  •   Aaron M    15 年前

    我认为这取决于你在寻找什么。这些信息重要吗?如果信息是关键的,那么您需要抛出一个异常,特别是如果不这样做会导致进一步的错误。

    我喜欢返回-1,如果找不到匹配项,只是因为您总是知道如果找不到匹配项,函数将返回什么。如果你把它建立在集合的大小上,那么你就永远不会确切知道返回到那里的是什么。

    我也不太喜欢在输出参数中返回它,只是因为它有点复杂,您必须问问自己,对于这样的函数,是否真的需要增加复杂性。

        5
  •  1
  •   Adrian McCarthy    15 年前

    我采用的一个经验法则是:

    当有疑问时,要像STL那样做。

    std::find 返回序列结尾以外的迭代器。

    决定是抛出异常还是返回某种类型的错误值可能很困难。只有较高级别的代码知道是否有异常情况,但必须由较低级别的代码做出决定。

        6
  •  1
  •   dirkgently    15 年前

    我主要使用“返回集合大小”方法,因为它类似于STL迭代器的工作方式。

    迭代器,如果您使用这些作为访问器/变元器,则基于的算法会将迭代器返回到集合末尾(可以将其与 coll.end() . 在我看来这绝对是件好事。

        7
  •  0
  •   Treb    15 年前

    一般来说,我不会在任何情况下使用您的第一个和第四个示例。返回集合的大小通信不够清晰,以至于找不到任何项,IMO(请考虑一个错误)。我看不需要返回两个输出,当您可以通过只返回一个来完成相同的操作时(如 -1 = ItemNotFound )。

    其余两种方法的使用取决于程序中的具体情况。如果项目 应该 在你的收藏中,这种情况是 X射线 . 在这种情况下,抛出异常是可以的。

    另一方面,如果项目 能够 在你的收藏中,这是一个 有规律的 情况,所以例外是不够的。

    如果编写的函数可能在两种情况下都使用,则不应引发异常。因为你永远不知道什么时候你可能需要一个函数,而不是你首先想到的,我的建议是返回-1 未找到项目 . 您还可以将这种方法用于其他函数,从而使您的代码基更加一致。

        8
  •  0
  •   pgast    15 年前
    std::pair<size_t,bool>
    
    class ItemCollection
    {
    public:
        typedef std::pair<size_t,bool> RC;
        // Return size of collection if not found.
        RC getIndex(Item * inItem)
        {
            size_t idx = 0;
            for (; idx != mItems.size(); ++idx)
            {
                if (inItem == mItems[idx])
                {
                    return RC(idx,true);
                }
            }
            return RC((-1),false);
        }
    

    对我来说,在这个时代,使用魔法数字是可疑的