如何解决带有模板参数的自定义 C++ 异常
我正在尝试学习如何使用带有模板参数的自定义 C++ 异常。这是一个我试图编译失败的虚拟程序:
#include <string>
#include <iostream>
template <class T>
class MyException : public std::exception {
public:
T error;
MyException(T err) { error = err; };
};
int main(void) {
try {
std::string err = "String error";
throw MyException<std::string>(err);
return 0;
}
catch (MyException e) {
std::cout << e << std::endl;
return 1;
};
}
这是我得到的错误:
<source>: In function 'int main()':
<source>:18:9: error: invalid use of template-name 'MyException' without an argument list
18 | catch (MyException e) {
| ^~~~~~~~~~~
<source>:18:9: note: class template argument deduction is only available with '-std=c++17' or '-std=gnu++17'
<source>:5:7: note: 'template<class T> class MyException' declared here
5 | class MyException : public std::exception {
| ^~~~~~~~~~~
<source>:19:22: error: 'e' was not declared in this scope
19 | std::cout << e << std::endl;
| ^
你能帮我为 C++11 修复它吗?
解决方法
你无法捕捉任何模板!您只能捕获特定类型,因此只能捕获模板的特定实例。所以你的代码应该是这样的(快速修复):
#include <string>
#include <iostream>
template <class T>
class MyException : public std::exception {
public:
T error;
MyException(T err) { error = err; };
};
int main(void) {
try {
std::string err = "String error";
throw MyException<std::string>(err);
return 0;
}
catch (const MyException<std::string>& e) {
std::cout << e.what() << std::endl;
return 1;
};
}
如果你需要一些方法来捕获这个模板的所有异常,你需要额外的继承层来为这个模板引入通用的唯一父类型:
#include <string>
#include <iostream>
class MyCommonException : public std::exception
{};
template <class T>
class MyException : public MyCommonException {
public:
T error;
MyException(T err) { error = err; };
};
int main(void) {
try {
std::string err = "String error";
throw MyException<std::string>(err);
return 0;
}
catch (const MyCommonException& e) {
std::cout << e.what() << std::endl;
return 1;
};
}
,
c++ 没有“模板捕获”,你只能捕获像
这样的实际类catch (MyException<string> e) { ... }
幸运的是 std::exception::what()
是虚拟的,因此您可以通过常量引用捕获 std::exception
并打印 what()
的结果。
catch (std::exception const &e) {
std::cout << e.what() << std::endl;
return 1;
};
并覆盖 what()
中的 MyException
以在此处创建正确的错误消息:
template <class T>
class MyException : public std::exception {
public:
T error;
MyException(T err) { error = err; };
const char* what() const noexcept override {
// apply logic to create error message
}
};
通过这种方式,您可以捕获从 std::exception
继承的任何内容。
我将尝试解决有关 C++ 模板的潜在混淆,而不是直接回答,以期防止更多此类问题出现。
粗略地说,语法 template <...> class MyException ... {};
不会在程序中引入任何“有形的”(类型或值)。没有类型 MyException
。每次遇到特定语法 MyException<...>
时,编译器都会实例化模板中的一个类。但是 MyException<...>
的每个实例对于模板参数的每个组合都是唯一的; MyException<A>
与 MyException<B>
不同,它们也可以称为 Bla
和 Pft
- 它们之间没有任何共同之处。
那么如何让 MyException<A>
和 MyException<B>
以共同的方式行动?好吧,就像处理两个不相关的类一样 - 使用继承和多态。
// define some common base that is NOT a template
class MyException : public std::exception {
public:
// put any common API's here ...
virtual std::string commonWork() = 0;
virtual ~MyException() {}
};
// now make each template inherit from the common base ...
template <class T>
class MyExceptionImpl : public MyException {
public:
T error;
MyExceptionImpl(T err) { error = err; }
std::string commonWork() override { return ""; }
};
现在你可以catch (MyException const& e) { ... }
。
@Marek 已经解释过我们无法在 C++ 中捕获模板。 对于您的自定义消息,您可以像这样打印它们:
#include <string>
#include <iostream>
template <class T>
class MyException : public std::exception {
public:
T error;
MyException(T err) { error = err; };
template<typename U>
friend std::ostream& operator <<(std::ostream &,const MyException<U>&);
};
template <typename T>
std::ostream& operator<<( std::ostream& o,const MyException<T>& e) {
return o<<e.error;
}
int main(void) {
try {
std::string err = "String error";
throw MyException<std::string>(err);
return 0;
}
catch (const MyException<std::string>& e) {
std::cout << e << std::endl;
return 1;
};
}
,
这个答案实际上只是将@Marek R 和@churill 已经提到的事情拼凑在一起。
在评论中你说你
- 不想捕捉
std::exception
,也不想使用std::exception::what()
。 - 想要专门打印
error
字段。
这只是使用自定义虚拟函数重新实现 what
功能,但您可以这样做:
#include <string>
#include <iostream>
class MyBaseException : public std::exception {
public:
virtual std::ostream& intoStream(std::ostream& stream) const = 0;
};
std::ostream& operator<<(std::ostream& stream,const MyBaseException& e) {
return e.intoStream(stream);
}
template <typename T>
class MyException : public MyBaseException {
public:
T error;
MyException(T err) { error = err; };
std::ostream& intoStream(std::ostream& stream) const override {
return stream << error;
}
};
int main(void) {
try {
std::string err = "String error";
throw MyException<std::string>(err);
return 0;
}
catch (const MyBaseException& e) {
std::cout << e << std::endl;
return 1;
};
}
由于只有类方法可以是虚拟的,如果你想使用 <<
操作符,你需要一个单独的虚拟函数,它可以访问 error
字段。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。