我的任务是在C ++类库中迁移错误处理的概念。之前简单返回bool(成功/失败)的方法应该被修改为返回一个Result
对象,该对象传达机器可读的错误代码和人类可读的解释(还有一些在这里无关紧要)。
遍历数千行代码容易出错,因此我尝试从编译器获得此任务的最佳支持。
我的结果类在其他成员方法中有一个构造函数,它构造代码的结果和代码的赋值运算符:
class Result
{
public:
typedef unsigned long ResultCode;
explicit Result(ResultCode code); // (1)
Result& operator=(ResultCode code); // (2)
};
备注:我通常会使用ResultCode
的枚举类来解决我的问题,但这不是一个选项。这是因为主要的设计目标是在不同的库中使用Result
,每个库都应定义自己的结果代码集,而不需要一个大的头文件来定义所有库的所有可能的结果代码。实际上,每个类都应该能够定义本地结果代码,以便可以从类头中获得可能的结果代码列表。因此,代码不能在Result
中枚举,它们必须由使用Result
类的类定义。
避免在
上隐式转换return true;
客户端代码中的语句,构造函数已声明为显式。但是在嵌套方法调用中,会出现另一个问题。说,我有一个方法
bool doSomething()
{
return true;
}
我在一个返回Result
对象的函数中使用它。我想转发嵌套调用的结果代码
Result doSomethingElse
{
Result result = doSomething();
return result;
}
使用Result
赋值运算符的当前实现,这不会给我一个编译器错误 - doSomething()的布尔返回值被隐式转换为unsigned long。
正如我在C ++文档中所读到的,只有构造函数和转换运算符可以声明为显式。
我的问题
答案 0 :(得分:3)
您的问题不在课程explicit
中:毕竟, 显式创建了它的新实例; bool -> long
不能禁止它。
我认为你不能禁止隐含促销ResultCode
。
你可以解决它。一种方法是使class ResultCode
{
unsigned long m_code;
public:
explicit ResultCode(unsigned long code) : m_code(code) {}
operator unsigned long () { return m_code; }
};
不为整数类型。那么,它可以有一个显式的构造函数。像
ResultCode
允许您在任何可以使用unsigned int
的地方使用ResultCode res = 5
并将其设为return ResultCode(5)
或ResultCode
,但不能调用期望Result
的函数(例如ResultCode
构造函数!),其中包含任何非return 5
的内容,如果函数必须返回ReturnCode
,也不会执行unsigned int
之类的内容。
否则,您可以使用模板重载来“捕获”任何不是typedef unsigned long ResultCode;
class Result
{
ResultCode m_par;
public:
template<typename T>
Result(T param) { static_assert(false); }
template<>
Result(ResultCode par): m_par(par) {}
};
int main()
{
ResultCode a = 5; //ok
//unsigned long a = 6; //also ok
//bool a = true; //error!
//int a = 7; //also error!!
Result b(a);
}
的内容并强制它成为错误
{{1}}
答案 1 :(得分:1)
使用Result的赋值运算符的当前实现,这不会给我一个编译器错误 - doSomething()的布尔返回值被隐式转换为unsigned long。
关于您发布的代码;它确实会导致错误error: no viable conversion from 'bool' to 'Result'
,see here。
需要一个显示您在代码中看到的行为的最小示例。在实际代码中可能存在其他构造函数或类型,这些构造函数或类型会对代码产生重大影响。
关于明确提出的问题......
为什么显式不允许赋值运算符或其他方法?
explicit
仅允许进行隐式转换,即编译器尝试为您生成转换的地方(bool
有特殊情况)。这种转换是构造函数和转换(或转换运算符)。
将构造函数或转换运算符标记为explicit
会阻止编译器进行转换,因此,如果需要转换,则需要明确它 - 作为执行此操作的一般动机,它使代码更加明确。需要权衡,因此在这两种情况下都应该明智地使用。一般建议是在有疑问时赞成explicit
。
例如;
struct Result {
Result(long src); // can be marked explicit
operator long() const; // can be marked explicit
};
是否有其他解决方案可以阻止赋值运算符的隐式类型转换?
赋值运算符特定于Result& operator=(Result&);
。在赋值本身中,没有转换。为了防止为赋值隐式创建Result
,需要将构造函数标记为explicit
。
要阻止从Result
创建ResultCode
,您可以不声明方法,也不能将其标记为已删除;
Result& operator=(ResultCode code) = delete;