[英]Using throw to replace return in C++ non-void functions
In C++ functions, is it a good practice to replace return
with throw
? 在C ++函数中,用
throw
替换return
是一个好习惯吗? For example, I have the following code 例如,我有以下代码
// return indices of two numbers whose sum is equal to target
vector<int> twoSum(vector<int>& nums, int target) {
for(int i=0; i<nums.size()-1; ++i)
for(int j=i+1; j<nums.size(); ++j)
{
if(nums[i] + nums[j] == target) return vector<int>{i, j};
}
// return vector<int>{};
throw "no solution";
}
The code above compiles with my GCC 7.2. 上面的代码用我的GCC 7.2编译。
In C++ functions, is it a good practice to replace return with throw?
在C ++函数中,用throw替换return是一个好习惯吗?
Return is not something that can be replaced by a throw in general . 返回不是一般可以被投掷取代的东西。
In exceptional cases where you have nothing to return, throwing an exception can be a valid way to exit the function. 在无法返回任何内容的特殊情况下,抛出异常可能是退出函数的有效方法。
Whether it is "good practice", and what case is "exceptional" are subjective. 是否是“良好实践”,什么案例是“特殊”是主观的。 For example, for a search function such as yours, it's hardly a surprise that there might not be a solution, and I would argue that throwing would not be appropriate.
例如,对于像你这样的搜索功能,可能没有解决方案并不奇怪,我认为投掷不合适。
There are often other alternatives to throwing. 投掷通常还有其他选择。 Compare your algorithm with something like
std::string::find
that returns the index of the start of a substring. 将您的算法与
std::string::find
,返回子字符串开头的索引。 In case where substring does not exist, it returns a "non-value" std::string::npos
. 如果substring不存在,则返回“非值”
std::string::npos
。 You could do the same and decide that the index -1 is returned when a result is not found. 您可以这样做,并确定在找不到结果时返回索引-1。 There is also a generic way to add non-value representation to a type in cases where none of the existing representations can be reserved for the purpose:
std::optional
. 在没有为此目的保留任何现有表示的情况下,还有一种向表单添加非值表示的通用方法:
std::optional
。
PS A vector is probably not a good choice for returning a pair of numbers. PS向量可能不是返回一对数字的好选择。
std::pair
might be better, or a custom class if you have good names for the numbers. std::pair
可能更好,或者如果你有数字的好名字,可以是自定义类。
The concepts of this answer are taken from the C++ Programming language by Bjarne Stroustrup. 这个答案的概念来自Bjarne Stroustrup的C ++编程语言。
Yes, exception-throwing can be used as returning value method. 是的,异常抛出可以用作返回值方法。 An example is the following for a binary tree search function:
以下是二叉树搜索功能的示例:
void fnd(Tree∗ p, const string& s)
{
if (s == p−>str) throw p; // found s
if (p−>left) fnd(p−>left,s);
if (p−>right) fnd(p−>right,s);
}
Tree∗ find(Tree∗ p, const string& s)
{
try {
fnd(p,s);
}
catch (Tree∗ q) {
// q->str==s
return q;
}
return 0;
}
However, it should be avoided because: 但是,应该避免,因为:
Apart from that, there are further limitations: 除此之外,还有其他限制:
An exception is an object thrown to represent the occurrence of an error. 异常是抛出一个表示错误发生的对象。 It can be of any type that can be copied but it is strongly recommended to use only user-defined types specifically defined for that purpose.
它可以是任何可以复制的类型,但强烈建议仅使用专门为此目的定义的用户定义类型。 Exceptions allow the programmer to explicitly separate error-handling code from "ordinary code" making the program more readable.
例外允许程序员明确地将错误处理代码与“普通代码”分开,使程序更具可读性。
First of all, exceptions are for managing synchronous events , not asynchronous ones. 首先,例外是管理同步事件 ,而不是异步事件 。 This is one first limitation.
这是第一个限制。
One might think of the exception-handling mechanisms as simply another control structure, an alternative way of returning a value to a caller. 有人可能会将异常处理机制视为另一种控制结构,这是将值返回给调用者的另一种方法。
This has some charm but should be avoided because it is likely to cause confusion and inefficiencies. 这有一些魅力,但应该避免,因为它可能会导致混乱和效率低下。 Stroustrup suggests:
Stroustrup建议:
When at all possible stick to the "exception handling is an error handling" view.
什么时候可能坚持“异常处理是一个错误处理”视图。 When this is done code is separated into two categories: ordinary code and error handling code.
完成此操作后,代码分为两类:普通代码和错误处理代码。 This makes the code more comprehensible.
这使代码更易于理解。 Furthermore, the implementations of the exception mechanisms are optimized based on the assumption that this simple model underlies the use of the exception.
此外,异常机制的实现基于这个简单模型基于异常的使用的假设而被优化。
So basically using exceptions to return value should be avoided because 所以基本上应该避免使用异常来返回值,因为
There are programs that for practical or historical reasons cannot use exceptions (neither as error handling so even less): 有些程序由于实际或历史原因不能使用异常(既不是错误处理,也不是更少):
throw
to catch
alternative error handling methods must be used. throw
到catch
传播的最大时间的工具,则必须使用其他错误处理方法。 news
and delete
) rather than relying on some systematic scheme such as resource handles ( string
s vector
s). news
和delete
非系统地管理免费存储)而不是依赖于某些系统方案,例如资源句柄( string
vector
s)。 In the above cases, traditional pre-exception methods are preferred. 在上述情况下,传统的异常前方法是首选。
return
and throw
have two different purposes and should not be considered interchangeable. return
和throw
有两个不同的目的,不应该被认为是可以互换的。 Use return
when you have a valid result to send back to the caller. 如果您有有效的结果,请使用
return
返回给调用者。 On the other hand, use throw
when some exceptional behavior occurs. 另一方面,当出现一些异常行为时使用
throw
。 You can get an idea of how other programmers use throw
by using functions from the standard library and taking note of when they throw exceptions. 您可以通过使用标准库中的函数并记录它们何时抛出异常来了解其他程序员如何使用
throw
。
In addition to the other answers, there's also performance : Catching an exception incurs a run-time overhead (see this answer ) compared to an if
clause. 除了其他答案之外,还有性能 :与
if
子句相比,捕获异常会产生运行时开销(请参阅此答案 )。
(Of course, we're talking about microseconds... Whether or not this is relevant depends on your specific use case.) (当然,我们谈论的是微秒......这是否相关取决于您的具体用例。)
A function should throw an exception when it is unable to meet its postcondition. 函数在无法满足其后置条件时应抛出异常。 (Some functions may also throw exceptions when their preconditions are not met; that's a different topic that I won't get into here.) Therefore, if your function must return a pair of integers from the vector summing to the given target, then it has no choice but to throw an exception when it can't find one.
(有些函数也可能在不满足前提条件时抛出异常;这是我不会在这里讨论的另一个主题。)因此,如果你的函数必须从向量求和到给定目标返回一对整数,那么它别无选择,只能在无法找到异常时抛出异常。 But if the function's contract allows it to return a value to indicate it was unable to find such a pair, then it should not throw an exception, since it has the ability to fulfill the contract.
但是如果函数的契约允许它返回一个值来表明它无法找到这样的一对,那么它就不应该抛出异常,因为它有能力履行合同。
In your case: 在你的情况下:
std::optional<std::pair<int, int>>
. std::optional<std::pair<int, int>>
。 It never needs to throw an exception because it can just return an empty optional when it can't find an appropriate pair. std::pair<int, int>
, then it should throw an exception because there is no sensible value to return when it can't find an appropriate pair. std::pair<int, int>
,那么它应该抛出一个异常,因为当它找不到合适的对时没有合理的值返回。 Generally, C++ programmers prefer to write functions that don't need to throw exceptions in order to report mere "disappointments", such as search failures, that are easily anticipated and handled locally. 通常,C ++程序员喜欢编写不需要抛出异常的函数,以报告容易预期和本地处理的“失望”,例如搜索失败。 The advantages of returning values rather than throwing exceptions are discussed extensively elsewhere so I won't rehash that discussion here.
在其他地方广泛讨论了返回值而不是抛出异常的优点,因此我不会在此重复讨论。
Thus, declaring "failure" by throwing an exception is usually limited to the following cases: 因此,通过抛出异常来声明“失败”通常仅限于以下情况:
What you have mentioned is not good programming practice. 你提到的不是很好的编程习惯。 Replacing a
return
statement with throw
is not acceptable in production-level code, especially with automated testing platforms that generate lists of all exceptions as a way of proving or disproving certain functionality. 使用
throw
替换return
语句在生产级代码中是不可接受的,特别是对于生成所有异常列表的自动化测试平台,作为证明或反驳某些功能的方式。 You have to take into consideration the needs of Software Testers when designing your code. 在设计代码时,您必须考虑软件测试人员的需求。
throw
is simply not interchangeable with return
. throw
与return
无法互换。 throw
is used to signal that a program error has occurred by some unexpected phenomena. throw
用于表示某些意外现象发生了程序错误 。 return
is used to signal method completion. return
用于表示方法完成。 It is common to use return
to transmit error codes, but return values do not cause the program to be interrupted in the same way as throw
does. 通常使用
return
来传输错误代码,但返回值不会导致程序以与throw
相同的方式被中断。 Additionally, throw
has the power to terminate a program if it is not handled correctly. 此外,如果没有正确处理,
throw
可以终止程序。
Essentially, it is good practice to use throw
when a significant error is detected within the method, but there should always be a clean return if such an error is not detected. 从本质上讲,当在方法中检测到重大错误时使用
throw
是一个好习惯,但如果没有检测到这样的错误,应该总是有一个干净的返回。 Can you make that substitution? 你能做出替代吗? Maybe, and maybe it will work logically...but it's not good practice and certainly not a popular implementation.
也许,也许它会在逻辑上起作用......但这不是一个好的做法,当然也不是一个流行的实现。
No, throw
is not a good semantic replacement for return
. 不,
throw
不是return
的良好语义替代品。 You only want to use throw
when your code has done something that it should not be doing, not to signify the perfectly valid negative result of a function. 你只想在你的代码完成它不应该做的事情时使用
throw
,而不是表示函数的完全有效的否定结果。
As a general rule, exceptions are meant to signify when something abnormal or unexpected has happened during the execution of your code. 作为一般规则,异常意味着表示在执行代码期间发生异常或意外事件的时间。 Looking at the purpose of your function, the occurrence of no two integers in the passed vector summing to
target
is a very possible result of the function, so that result is not abnormal and thus should not be treated as exceptional. 看一下你的函数的目的,在传递的向量求和到
target
中没有两个整数的出现是函数的一个非常可能的结果,因此结果不是异常的,因此不应该被视为异常。
Just because the function is throwing as the last call, doesn't mean it is replacing return. 仅仅因为函数作为最后一次调用而抛出,并不意味着它正在替换return。 It is just the flow of logic.
这只是逻辑的流动。
The question shouldn't be : 问题不应该是:
is it a good practice to replace return with throw?
用throw代替return是一个好习惯吗?
Instead it should be about : how to define your API and contracts . 相反,它应该是: 如何定义您的API和合同 。
If You want to guarantee to the users of the function that vector
is never empty, then throwing an exception is a good idea. 如果你想向函数的用户保证
vector
永远不会为空,那么抛出异常是一个好主意。
If you want to guarantee that your function doesn't throw exceptions instead returns an empty vector
on certain conditions, then throwing is a bad idea. 如果你想保证你的函数不会抛出异常而是在某些条件下返回一个空
vector
,那么抛出是一个坏主意。
In both cases, the user has to know, what actions they have to take to correctly use your function. 在这两种情况下,用户必须知道他们必须采取什么行动才能正确使用您的功能。
Your question is language agnostic. 你的问题与语言无关。
Return and Throw have different purposes. 返回和投掷有不同的目的。
Bonus Content (from Steve McConnell's Code Complete 2) 奖金内容 (来自Steve McConnell的代码完成2)
Here are all the alternatives available to you when you encounter a situation where you cannot return normally: 当您遇到无法正常返回的情况时,以下是您可以使用的所有备选方案:
Further, you do not need to pick only one of the above options. 此外,您不需要只选择上述选项之一。 You can do a combination like, for example, logging to file and displaying a message to the user.
您可以执行组合操作,例如,记录到文件并向用户显示消息。
What approach you should take is a question of Correctness vs Robustness . 你应该采取什么方法是正确性与健壮性的问题。 Do you want your program to be absolutely correct and shutdown when an erroneous situation is encountered or do you want your program to be robust and continue with execution when at some point it fails to follow the desired path?
您是否希望您的程序绝对正确并在遇到错误情况时关闭,或者您希望程序是否健壮并在某些时候继续执行它无法遵循所需的路径?
I think it is the wrong question asking if return can be replaced by throw . 我认为这是一个错误的问题,询问是否可以用throw 代替 return 。 return and throw are doing two very different things:
return和throw正在做两件截然不同的事情:
The alternative reaction on errors could be to return special error code values. 对错误的替代反应可能是返回特殊错误代码值。 This has some disadvantages, eg:
这有一些缺点,例如:
In your example the answer depends on the semantics of the function: 在您的示例中,答案取决于函数的语义:
If an empty vector is an acceptable return value, then you should use return if no pair is found. 如果空向量是可接受的返回值,那么如果没有找到对,则应该使用return 。
If it is expected that there MUST be matching pairs always, then finding no pair obviously is an error. 如果预计总是必须匹配对,那么找不到对显然是一个错误。 You should use throw in this case.
在这种情况下你应该使用throw 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.