【问题标题】:What's the right way to overload the copy assignment operator of an all-const type?重载全常量类型的复制赋值运算符的正确方法是什么?
【发布时间】:2012-09-11 13:12:09
【问题描述】:

假设我有这样一个结构:

struct Foo
{
  const int bar;
  const char baz;

  Foo& operator=(const Foo& other)
  {
    memcpy(this,&other,sizeof(Foo)); //How am I supposed to write this decently?
    return *this;
  }
}

我希望 Foo 的所有字段都是最终的,并且我希望 Foo 类型的变量的行为就像其他原始值类型一样。说,int,我们当然可以这样写:

 int i = 0;
 i = 42;

 Foo foo = {007,'B'}
 foo = {42,'X'}

但是对于我可怜的 Foo 类型,我是否必须求助于 memcpy 之类的方法来解决类型安全检查?我知道我可以删除 const 修饰符,将字段标记为私有并添加一些 getter,但这不是重点。我只是想知道是否有一种体面的方式来编写 = 运算符的内容。

提前致谢!

~~~~~

查看以下示例:

//If the = op is not implemented, this won't compile
Foo stat;
for(int i=0;i!=100;++i)
{
  stat = func(i);
  if(stat.bar == 0)...
}

//But weird thing is, if I declare the 'stat' inside the for block, it works just fine with gcc
for(int i=0;i!=100;++i)
{
  Foo stat = func(i); 
  //printf("%p\n",&stat); => same variable same address!!
  if(stat.bar == 0)...
}

这对你有意义吗?

【问题讨论】:

  • 这毫无意义(这就是为什么你不能让它工作)。你想要 const 一个“全常量”类型是可变的?下定决心。不,甚至memcpy 也做不到。最终结果是未定义的行为。准备好看到编译器做你没有要求它做的事情。
  • @R.MartinhoFernandes - 为什么我不能使用 memcpy?如果它有确定的大小和确定的地址,为什么我不能往它占用的内存卡盘里写东西呢?
  • 这不是很明显吗?因为成员是const!

标签: c++ overloading operator-keyword


【解决方案1】:

在 C++ 中,复制赋值对于 all-const 类型毫无意义。不要实现它。

不要在有意义的地方使用 all-const 类型,但请注意,这种类型不会表现得像 int,因为 C++ 中的 int 只是't const 除非你这样声明。

【讨论】:

  • 或者在使用现场制作对象const
  • @Xeo 是的——但前提是首先拥有const 或非const 对象是有意义的。完全可以想象,给定类型的对象永远不会改变。
【解决方案2】:

在这种情况下体面的写法是:

Chunk& operator=(const Foo& other) = delete;

(或private pre-C++11)

如果你所有的成员都是const,你到底为什么要改变他们?

【讨论】:

  • 也许不错,但没必要。它会因const 成员的存在而被隐式删除。
【解决方案3】:

没有什么好办法,我同意其他答案,您应该重新考虑设计。

但如果您仍然认为这对您的问题最不利,还有几个选择:

Foo& operator=(const Foo& other)
{
    const_cast<int&>(bar) = other.bar;
    const_cast<char&>(baz) = other.baz;
    return *this;
}

或者

#include <memory>

// Foo must never be used as a base class!
// If your compiler supports the c++11 final feature:
struct Foo final
{ /*...*/ };

Foo& operator=(const Foo& other)
{
    if (this != &other) {
        this->~Foo();
        new(this) Foo(other);
    }
    return *this;
}

【讨论】:

  • 如果实际对象的类型派生自Foo,则第二个会导致未定义的行为。永远不要这样做!标准在其对对象生命周期的讨论中包含此代码是一个严重的错误,因为它给人的印象是这实际上是有用的。
  • @aschepler 第二个解决方案看起来很酷,没有强制转换,没有手动内存操作,我就是喜欢它。非常感谢;)
  • 第一个在所有情况下都会导致未定义的行为。
  • 如果构造函数抛出,第二个也会导致未定义的行为。这两种选择都很危险。如果你需要修改一些东西,首先不要声明它const
猜你喜欢
  • 2020-12-18
  • 1970-01-01
  • 2021-10-28
  • 2016-03-24
  • 1970-01-01
  • 2021-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多