C ++:我可以使赋值运算符“显式”

时间:2016-07-19 07:06:52

标签: c++

我的任务是在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 ++文档中所读到的,只有构造函数和转换运算符可以声明为显式。

我的问题

  1. 为什么显式不允许赋值运算符或其他方法? IMO将允许任何方法显式化也很有意义。
  2. 是否有其他解决方案来阻止赋值运算符的隐式类型转换?

2 个答案:

答案 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;