【问题标题】:C++ overload assignment operatorC++ 重载赋值运算符
【发布时间】:2022-01-24 12:50:21
【问题描述】:

我目前正在努力使用赋值运算符。我总是想念一些东西。 你能帮帮我吗?

在这里查看 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; 不是赋值,而是构造。
  • 您的问题出在构造函数中,这是明确的。 “SpecialFloat”类的构造函数声明为“显式”
  • 支持@Eljay:从构造函数中删除explicit(并修复赋值运算符)使您的代码编译:Test of Compiler Explorer
  • 啊好的谢谢你:)
  • 与问题无关,但您在operator= 中忘记了return *this;

标签: c++ assignment-operator


【解决方案1】:

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

如果你写了SpecialFloat f{0.0f}; f = 1.0f[Demo]就可以了。

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

从另一个对象初始化一个对象。
语法
T object = other; (1)

在您的代码中,TSpecialFloat,一个类类型,otherfloat(不是 T 或派生自 T)。

复制初始化的效果是:
...
如果 T 是类类型,并且 other 类型的 cv 非限定版本不是 T 或派生自 T [...] 用户定义的转换序列,可以从 的类型转换em>other 到 T 是 检查并通过重载决议选择最好的。如果使用了转换构造函数,则转换的结果是 T 的 cv 非限定版本的右值临时 [...],然后用于直接初始化对象。

应检查从floatSpecialFloat 的用户定义转换。但是,复制初始化不考虑显式构造函数。

备注

复制初始化比 直接初始化:显式构造函数不转换 构造函数,并且不考虑进行复制初始化。

解决这个问题的一种方法是使用direct initialization,如果可能的话,用大括号代替圆括号,即SpecialFloat f{1.0f};[Demo]。 有一个关于preferring the {}-initializer syntax 的 C++ 核心指南。 此外,声明 single-argument constructors explicit 是一般性建议,因此我会保持用户声明的构造函数为显式。

另一种方法是使SpecialFloat 类成为一个聚合类,方法是删除用户声明的构造函数,并使用aggregate initializationSpecialFloat f = {1.0f};[Demo]

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


[编辑]

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

C++ 中变量初始化的简单指南:

  • 尽可能使用 =。
  • 仅对元素初始化器(容器和聚合)使用初始化器列表语法 {}。
  • 使用函数调用语法 () 调用构造函数,将其视为对象工厂。

因此:

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

此外,他建议将复制初始化与几乎总是自动的样式结合起来。回到原始 OP 的问题,这将允许我们保持 SpecialFloat 类不变并编写 auto f = SpecialFloat{1.0f}; [Demo]

尽管他承认他的指导方针与前面提到的 preferring the {}-initializer syntax 的 C++ 核心指导方针相冲突。

【讨论】:

  • 谢谢,这真的很有帮助。我通常害怕使构造函数显式,但在这种情况下它失败了。所以我了解到,SpecialFloat f = 1.0f;SpecialFloat f{1.0f}; 不同,正如您提出的核心准则,我将坚持使用 {} Initialiser :)
  • 很高兴能提供帮助。我建议几乎在任何地方都使用 {} 初始化程序。但要注意像std::vector&lt;int&gt; vi{5, 10}; 这样的情况,其中{} 实际上意味着std::initializer_list constructor Demo。 Scott Meyers 在 Effective Modern C++ 的第 7 章中指出了大括号初始化的一些缺点(在创建对象时区分 () 和 {}。)
【解决方案2】:

有以下几个问题。

SpecialFloat f = 1.0f;

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

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

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

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

SpecialFloat f = 1.0f;

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

【讨论】:

  • thx 这是一个有用的评论:) 我错过了这个*this。但是,我通常喜欢使构造函数显式化。我将尝试应用@rturrado 解决方案。如果这对我不起作用,我将删除显式关键字:)
猜你喜欢
  • 2013-03-30
  • 2016-08-30
  • 1970-01-01
  • 2018-12-27
  • 2012-04-22
  • 2015-06-01
  • 2012-08-17
  • 2011-01-27
  • 2011-05-31
相关资源
最近更新 更多