【问题标题】:Not All Control Paths Return a Value? Warning不是所有的控制路径都返回一个值?警告
【发布时间】:2013-04-23 18:00:38
【问题描述】:

我有一小段代码在编译时给我以下警告:

'BGOLUB::Containers::Stack::Pop' :并非所有控制路径都返回值

代码如下:

template<typename T>
T Stack<T>::Pop()                                                                           
{

try
{
    if (m_index<0) throw OutOfBoundsException(m_index);

    --m_index;
    return(m_array[m_index]);
}

catch(OutOfBoundsException&)
{
    cerr<<"Underflow Index = "<<m_index<<endl;
}

catch(...)
{
    cerr<<"Unhandled Error Occured"<<endl;
}
}

有什么建议吗?

非常感谢!

【问题讨论】:

  • 你需要在你的 catch 块中返回一些值

标签: c++ error-handling compiler-warnings


【解决方案1】:

有什么建议吗?

编译器会给你最好的建议。并非您的函数中的所有控制路径都包含return 语句,并且您的函数应该返回一个值。

如果抛出异常并将控制权转移到 catch 处理程序,则该处理程序将向 cerr 打印一些内容,然后在函数的末尾流出,而实际上 return 没有任何内容。

这是未定义的行为。根据 C++11 标准的第 6.6.3/2 段:

[..] 从函数末尾流出相当于没有值的返回; 这会导致未定义 值返回函数中的行为

对于默认可构造值,您可以通过在函数结束前添加 return T() 语句来解决此问题:

template<typename T>
T Stack<T>::Pop()
{
    try
    {
        // ...
    }
    catch (OutOfBoundsException&)
    {
        // ...
    }
    catch (...)
    {
        // ...
    }

    return T();
//  ^^^^^^^^^^^
}

然而,更合理的方法是Pop() 吞下异常,而是重新抛出它Pop() 没有关于如何从这种情况下发生的错误中恢复的战略级信息:

template<typename T>
T Stack<T>::Pop()
{
    try
    {
        // ...
    }
    catch (OutOfBoundsException&)
    {
        // ...
        throw; // <== Re-throw after printing the diagnostic
    }
    catch (...)
    {
        // ...
        throw; // <== Re-throw after printing the diagnostic
    }
}

如果记录错误消息的责任根本不属于Pop(),那就更好了,因为在这个意义上Pop()可能应该被具有不同要求的代码重用(有些人可能不希望要记录任何内容,有些人可能希望将消息记录到文件中,有些人可能希望以不同的语言记录消息,等等)。

因此,您的函数的更合理版本实际上是:

template<typename T>
T Stack<T>::Pop()                
{
    if (m_index<0) throw OutOfBoundsException(m_index);
    --m_index;
    return(m_array[m_index]);
}

一般来说,您应该尝试(不是双关语)避免try/catch 块,除非您必须:

  • 翻译异常
  • 从错误中恢复(但您需要战略知识才能做到这一点)

如果这不是你的任务(就像上面 Pop() 这样的函数的情况),在大多数情况下,最好的办法是根本不处理异常并让它们向上传播调用堆栈。

引用Dave Abrahams

通常处理异常的最佳方法是根本不处理它们。如果您可以让它们通过您的代码并允许析构函数处理清理,那么您的代码会更干净。

为避免泄漏内存、资源或一般责任,请使用适当的 RAII 包装器编写异常安全的代码。 this two-part talk by Jon Kalb 给出了这方面的优秀指南。

特别是,避免编写catch (...) 处理程序:发明异常是为了防止程序员忽略错误,将它们全部吞入通用处理程序而不重新抛出它们是忽略它们的最佳方法。


注意:

注意,Pop() 的实现有点问题:如果 T 的复制构造函数或移动构造函数在将元素返回给调用者时抛出,在你已经修改了堆栈指针之后会发生什么?

这就是 C++ 标准库定义两个独立函数 pop()top() 的原因:因为它允许提供 强保证,即为您的 pop() 操作提供事务语义- 要么元素被移除而不抛出异常,要么函数完全没有效果。

【讨论】:

  • 扩展最后一点:在可以处理的情况下捕获异常。记录错误并不能处理它。
  • @sftrabbit:是的,一般来说是处理或翻译。但你是对的,记录不是处理。我应该澄清一下,谢谢
  • @MichaelDorgan:谢谢,很高兴你能欣赏它
【解决方案2】:

您需要重新抛出异常,或者在函数结束时返回可能是 T() 的内容。

【讨论】:

    【解决方案3】:

    当异常被抛出时,它会被两个 catch 语句之一捕获。但是,他们仍然需要 return 来自函数的值。您可以在函数的末尾放置一个return 语句。

    但是,如果Pop 因为Stack 为空而引发异常,那么让异常传播到函数之外更有意义。为什么Pop 自己会尝试处理异常情况?

    【讨论】:

      【解决方案4】:

      我建议在所有if 语句中使用方括号,即使是单行正文。它们不是绝对必要的,没有它们你可以编写完全合法的代码,但是它们使你的代码更具可读性,并且这样的错误会更容易发现。

      此外,您似乎对异常的工作原理存在根本性的误解。如果您的代码遇到异常,它将直接跳转到catch 块,并且不会执行try 块中的任何后续代码。因此,您的try 块中的return 语句将永远无法到达,并且您的函数将不会返回任何内容,因为catch 块缺少return 语句。

      您可以通过在 catch 块中添加 return 语句来解决此问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-04-08
        • 2014-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多