C++ 重载赋值运算符

C++ overload assignment operator

我目前正在为赋值运算符而苦恼。我一直在想念一些东西。 你能帮帮我吗?

在这里查看 https://godbolt.org/z/rfvTqcjoT

class SpecialFloat
{
    public:
    explicit SpecialFloat(const float f);

    SpecialFloat& operator=(const float f);
    private:
    float m_float;
};


SpecialFloat::SpecialFloat(const float f):
m_float(f)
{

}


SpecialFloat& SpecialFloat::operator=(const float f)
{
    m_float = f;
}

int main()
{
    SpecialFloat f = 1.0f;
}

为什么我的运算符重载不起作用?

<source>(27): error C2440: 'initializing': cannot convert from 'float' to 'SpecialFloat'
<source>(27): note: Constructor for class 'SpecialFloat' is declared 'explicit'

或者赋值运算符可以不接受自定义类型吗?

SpecialFloat f = 1.0f;无法执行从1.0ff的赋值,因为f还不存在。我们只是在创造它。

如果你写 SpecialFloat f{0.0f}; f = 1.0f [Demo].

就可以了

SpecialFloat f = 1.0f; 正在做 copy initialization (1)

Initializes an object from another object.
Syntax
T object = other; (1)

在您的代码中 TSpecialFloat,一个 class 类型,而 other 是一个 float(不是 T 或派生的来自 T).

The effects of copy initialization are:
...
If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T [...] user-defined conversion sequences that can convert from the type of other to T are examined and the best one is selected through overload resolution. The result of the conversion, which is a rvalue temporary [...] of the cv-unqualified version of T if a converting constructor was used, is then used to direct-initialize the object.

应检查从 floatSpecialFloat

User-defined 转换。但是,copy-initialization.

不考虑显式构造函数

Notes

Copy-initialization is less permissive than direct-initialization: explicit constructors are not converting constructors and are not considered for copy-initialization.

解决此问题的一种方法是使用 direct initialization, and, if possible, with braces instead of parentheses, i.e. SpecialFloat f{1.0f}; [Demo]。 有一个 C++ 核心指南建议 preferring the {}-initializer syntax。 此外,声明 single-argument constructors explicit 是一般建议,因此我会将 user-declared 构造函数保持为显式。

另一种方法是通过删除 user-declared 构造函数并使用 aggregate initialization, SpecialFloat f = {1.0f}; [Demo].

使 SpecialFloat class 成为聚合

最后,正如其他人评论的那样,请注意赋值运算符的签名是 SpecialFloat& operator=(const float f),这表明 SpecialFloat& 必须被 returned。所以首先,用 m_float = f; 更新对象;然后,return 它与 return *this;.


[编辑]

我刚从 Arthur O'Dwyer 的文章中看到这篇文章,The Knightmare of Initialization in C++ 他基本上赞成复制初始化而不是用大括号直接初始化,以提高代码的可读性。

Simple guidelines for variable initialization in C++:

  • Use = whenever you can.
  • Use initializer-list syntax {} only for element initializers (of containers and aggregates).
  • Use function-call syntax () to call a constructor, viewed as an object-factory.

Thus:

int i = 0;
std::vector<int> v = {1, 2, 3, 4};
Widget w(name, price, quantity);

此外,他建议将复制初始化与 Almost Always Auto 样式结合起来。回到最初的 OP 问题,这将使我们能够保持 SpecialFloat class 不变并写入 auto f = SpecialFloat{1.0f}; [Demo].

他承认他的指南与前面提到的 preferring the {}-initializer syntax 的 C++ 核心指南冲突。

有以下几个问题。

SpecialFloat f = 1.0f;

表示您正在尝试将浮点值分配给 SpecialFloat 对象。如果 SpecialFloat 的构造函数采用浮点参数并且构造函数未标记为显式,则此方法有效。但是在您的代码中,您将构造函数标记为显式。所以对象没有被创建并抛出错误。如果您想了解有关显式构造函数的更多信息,请阅读 What does the explicit keyword mean?

赋值运算符重载函数应该return SpecialFloat 对象。你没有 returning 任何错误的东西。它应该 return SpecialFloat 对象如下。

SpecialFloat& SpecialFloat::operator=(const float f)
{
    m_float = f;
    return *this;
}

你对赋值运算符重载函数调用的理解是错误的。当您尝试将对象分配给已创建的对象时,将调用赋值运算符重载函数。

SpecialFloat f = 1.0f;

以上语句试图创建一个对象。所以在这种情况下不会调用赋值运算符重载函数。