【问题标题】:Objects that can be initialized but not assigned可以初始化但不能赋值的对象
【发布时间】:2017-06-17 22:11:29
【问题描述】:

我需要创建一个类,它的对象可以初始化但不能赋值。

我想也许我可以通过不定义赋值运算符来做到这一点,但是编译器使用构造函数来进行赋值。

我需要这样:

Object a=1;    // OK
a=1;           // Error

我该怎么做?

【问题讨论】:

  • 编译器不使用构造函数进行赋值。它使用构造函数创建Object 类型的对象,然后使用赋值运算符将该对象分配给a
  • 出于好奇,这个的用例是什么?
  • 很难用细节来解释,但它是一个“参数传输”类,几乎兼容所有字符串类型(char、std::string、CString 和我的程序中使用的一些其他类型,以防止意外覆盖。我需要它,因为它是一个很难使用模板重写的旧项目。以前,所有参数都通过 char * 传递,这意味着危险。
  • @MooingDuck -- 代码中的第一行需要一个构造函数,该构造函数可以使用int类型的参数和一个可访问的复制构造函数调用乙>。但问题是关于第二行,这就是我所说的。
  • @MooingDuck -- 它需要复制构造函数,而不是复制赋值运算符

标签: c++ initialization variable-assignment


【解决方案1】:

使a const 可以解决问题

const Object a=1;    // OK

现在您将无法为a 分配任何值,因为a 被声明为const。注意,如果将a声明为const,则需要在声明时初始化a

一旦您将a 声明为const 并对其进行了初始化,您将无法为a 分配任何其他值

 a=1;   //error

【讨论】:

  • +1 虽然其他答案解决了 OP 在他们的段落中提出的问题,但我的直觉说这正是他们打算问的内容。
  • 如果您希望它应用于Object 的所有实例,请在一个(或多个)成员变量上使用const。瞧,赋值运算符默认删除。
【解决方案2】:

你可以delete赋值运算符:

#include <iostream>
using namespace std;

struct Object
{
    Object(int) {}
    Object& operator=(int) = delete;
};

int main()
{
    Object a=1;    // OK
    a=1;           // Error
}

替代解决方案

您可以使用explicit 关键字:

#include <iostream>
using namespace std;

struct Object
{
    explicit Object(int) {}
};

int main()
{
    Object a(1);    // OK - Uses explicit constructor
    a=1;           // Error
}

更新

正如用户 2079303 在 cmets 中提到的:

值得一提的是,替代解决方案不会阻止像 a=Object(1) 这样的常规复制/移动分配

这可以通过使用来避免:Object&amp; operator=(const Object&amp;) = delete;

【讨论】:

    【解决方案3】:

    我希望通过不定义赋值运算符来实现这一点

    这不起作用,因为复制赋值运算符(以const Object&amp; 作为参数)是隐式生成的。而当你写a = 1时,生成的复制赋值运算符会被尝试调用,并且1可以通过转换构造函数Object::Object(int)隐式转换为Object;然后a = 1; 工作正常。

    您可以将int 的赋值运算符显式声明为deleted (C++11 起);这将在重载决议中的复制赋值运算符之前被选择。

    如果函数过载,则首先发生过载分辨率,并且如果选择删除的功能,则该程序仅形成。

    例如

    struct Object {
        Object(int) {}
        Object& operator=(int) = delete;
    };
    

    还有一些其他具有副作用的解决方案。您可以将Object::Object(int) 声明为explicit 以禁止从intObject 的隐式转换,然后使a = 1 失败。但请注意,这也会使 Object a = 1; 失败,因为复制初始化不考虑 explicit 构造函数。或者你也可以将复制赋值操作符标记为已删除,但这也会导致Objects 之间的赋值失败。

    【讨论】:

    • =delete 之前我们会声明它是私有的并且从不定义它。或者使用 Boost 方便的混音。
    • 我的意思是,在可以声明已删除的函数(从 C++11 开始可用)之前,我们将改为将该函数声明为私有。
    • 我明白了,是的,这就是 C++11 之前的方法。
    【解决方案4】:

    我该怎么做?

    选项 1:

    制作构造函数explicit

    struct Object
    {
       explicit Object(int in) {}
    };
    

    选项 2:

    delete 赋值运算符。

    struct Object
    {
       Object(int in) {}
       Object& operator=(int in) = delete;
    };
    

    您可以使用以上两个选项。

    struct Object
    {
       explicit Object(int in) {}
       Object& operator=(int in) = delete;
    };
    

    选项 3:

    如果你不想在初始化后进行任何赋值,你可以delete赋值运算符以Object为参数类型。

    struct Object
    {
       explicit Object(int in) {}
       Object& operator=(Object const& in) = delete;
    };
    

    这将阻止使用:

    Object a(1);
    a = Object(2); // Error
    a = 2;         // Error
    

    【讨论】:

      【解决方案5】:

      删除的函数仅从 C++11 开始可用,对于较旧的编译器,您可以将赋值运算符设为 private

      struct Object
      {
      Object(int) {}
      private: 
      Object& operator=(int);
      };
      

      编译器现在会为

      抛出错误
      Object a=1; //ok
      a=2; // error 
      

      但你仍然可以这样做

      Object a=1,b=2;
      b=a;
      

      因为编译器不会阻止生成默认赋值运算符。所以标记默认分配private 将解决这个问题。

      struct Object
      {
      Object(int) {}
      private: 
      Object& operator=(Object&);
      };
      

      【讨论】:

        猜你喜欢
        • 2013-05-07
        • 2019-11-16
        • 2023-03-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-04
        相关资源
        最近更新 更多