wmj*_*gla 2 c++ exception return-type c++11
我心里有这样的想法:
// This is called at multiple locations and each handles the returned object
// differently. Handling also differs depending on the returned object type.
void GetObject() {
// Check runtime condition
// Object1 and Object2 are completely dissimilar and do not share a base class.
if (condition1) {
// Operations to prepare for construction of Object1
throw Object1{ var1, var2, var3 };
}
if (condition2) {
// Operations to prepare for construction of Object2
throw Object2{ var4, var5 };
}
throw MyException{};
}
// Usage example
int main() {
try {
GetObject();
}
catch (const Object1& obj1) {
// Object1-specific handling
}
catch (const Object2& obj2) {
// Object2-specific handling
}
catch (const MyException& e) {
// Error handling
}
}
Run Code Online (Sandbox Code Playgroud)
奇怪的是,有关返回不同数据类型的问题的答案根本没有提到这种技术:
这有什么特别的原因吗?我能想到的唯一原因是它是非正统的,否则这似乎是最简单和最干净的方法 - 没有第 3 方库,不需要升级到 C++17,没有额外的抽象。
正如许多人在评论中指出的那样,std::variant可以很好地解决问题,但它需要 C++17 或 boost 库;不幸的是,我的项目目前无法使用其中任何一个。
感谢大家的投入。他们非常有帮助,让我思考各种设计选项并突出显示我忽略的领域。我选择了下面的答案,因为它直接解决了不考虑使用该技术的原因。其他 2 个答案提出了替代技术,因此如果您有类似的用例,请查看它们。对我来说,我最终决定使用输出参数作为解决方案,因为它很简单,而且我没有 C++17 也没有 boost 的限制:
bool GetObject(
std::unique_ptr<Object1>& obj1,
std::unique_ptr<Object2>& obj2) {
// Check runtime condition
if (condition1) {
// Operations to prepare for construction of Object1
obj1 = std::make_unique<Object1>(var1, var2, var3);
return true;
}
if (condition2) {
// Operations to prepare for construction of Object2
obj2 = std::make_unique<Object2>(var4, var5);
return true;
}
return false;
}
// Usage example
int main() {
std::unique_ptr<Object1> obj1;
std::unique_ptr<Object2> obj2;
if (!GetObject(obj1, obj2)) {
// Error handling
return -1;
}
if (obj1) {
// Object1-specific handling
}
else {
// Object2-specific handling
}
...
}
Run Code Online (Sandbox Code Playgroud)
如果对象不太大,静态分配对象(即在堆栈上)也可以代替 unique_ptr 。但这需要Object1和Object2具有可以告诉调用者该类是否已初始化的成员函数。
异常只是错误的多态机制。抛出异常是一个极其昂贵的操作:

资料来源:@Arash 的答案包含此基准。
在此基准测试中,无论概率有多大,返回错误代码的成本基本相同。 抛出异常可能会慢 100 倍以上。 它很贵,因为
只要您从不抛出异常,那么它们的开销就为零,但如果您抛出异常,那么您就会陷入疯狂的境地。这就是为什么,正如其名称所示,它们用于特殊情况,例如:
std::vector::at在常规程序执行中,永远不应该抛出异常。
这个主题已经在您链接的线程中讨论得很彻底,但只是为了给您一些选择:
std::variant(C++17)std::optionalstd::expected特定情况下的(C++17) 和(C++23)std::unique_ptr(C++11)中struct它只包含两种类型,以及一个表示哪个类型处于活动状态的标签
union,但实施起来很简单你的例子有点太小了,无法说明哪种方式在这里效果最好。