C++:我可以将赋值运算符设为"explicit"吗?

C++: Can I make an assignment operator "explicit"

本文关键字:explicit 我可以 赋值运算符 C++      更新时间:2023-10-16

我的任务是迁移C++类库中的错误处理概念。之前简单返回bool(成功/失败)的方法应进行修改,以返回Result对象,该对象传达机器可读的错误代码和人类可读的解释(以及其他一些在此无关紧要的内容)。

遍历数千行代码很容易出错,因此我试图从编译器那里获得对这项任务的最佳支持。

在其他成员方法中,我的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()的布尔返回值被隐式转换为无符号长。

正如我在C++文档中所读到的,只有构造函数和转换运算符可以声明为显式的。

我的问题

  1. 为什么不允许显式用于赋值运算符或其他方法?国际海事组织认为,允许任何方法都明确也是很有意义的
  2. 是否有其他解决方案可以防止赋值运算符的隐式类型转换

您的问题不在类Result中:毕竟,您显式地创建了它的新实例;explicit不能禁止它。

我认为你不能禁止bool -> long的隐性促销。

一种方法是使ResultCode不是为整数类型。然后,it可以有一个显式构造函数。类似的东西

class ResultCode 
{
unsigned long m_code;
public:
explicit ResultCode(unsigned long code) : m_code(code) {}
operator unsigned long () { return m_code; } 
};

将允许您在任何可以使用unsigned int并将其创建为ResultCode res = 5return ResultCode(5)的地方使用ResultCode,但不使用任何已经不是ResultCode的函数调用需要ResultCode的函数(如Result构造函数!),如果函数必须返回ReturnCode,也不执行类似return 5的操作。

否则,您可以使用模板重载来"捕获"任何不是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);
}

对于Result赋值运算符的当前实现,这不会给我带来编译器错误——doSomething()的布尔返回值被隐式转换为无符号长。

关于您发布的代码;它确实会导致错误error: no viable conversion from 'bool' to 'Result',请参阅此处。

需要一个显示您在代码中看到的行为的最小示例。实际代码中可能存在其他具有转换的构造函数或类型,这些构造函数或类型对您的代码具有实质性影响。


关于明确提出的问题。。。

为什么不允许显式用于赋值运算符或其他方法?

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

为了防止从ResultCode创建Result,您可以不声明该方法,也可以将其标记为已删除;

Result& operator=(ResultCode code) = delete;