【问题标题】:Why the copy constructor is called为什么调用复制构造函数
【发布时间】:2019-01-24 11:29:23
【问题描述】:

我无法理解为什么调用复制构造函数而不是编译错误。

我声明了两个类 A B ,它们是独立的。 A 不是基于/派生于 B。它们之间的唯一联系是在 B 中我使用将 B 转换为 A 的运算符 我已经定义了一个操作符 =,它将 B 的 const 引用作为参数。 在 main 中,我编写了以下 B = A 实例的实例。我预计会生成编译错误。但是运算符 = 被称为

class A {};

class B {
public:
    // conversion from A (constructor):
    B()
    {

        cout << "1." << endl;
    }
    B(const A& x)
    {
        cout << "4." << endl;
    }
    // conversion from A (assignment):
    B& operator= (const B& x)
    {
        cout << "3." << endl;
        return *this;
    }
    // conversion to A (type-cast operator)
    operator A() {
        cout << "2." << endl;
        return A();
    }
};

int main()
{
    A foo;
    B bar;    // calls constructor
    bar = foo;      // calls assignment
                    //foo = bar;      // calls type-cast operator
    char c;
    c = getchar();
    return 0;
}

我预计编译错误。但是打印了以下序列 1 4 3 。 我几乎不明白如何调用复制构造函数以及为什么 operator = 不会产生问题

谢谢

【问题讨论】:

  • 我不清楚您为什么认为应该生成错误。什么样的错误?您认为该程序有什么问题?
  • 我预计将生成以下编译错误 39:6: 错误: no match for 'operator=' (operand types are 'B' and 'A') 39:6: note: Candidate is : 23:5: 注意: B& B::operator=(const B&) 23:5: 注意: 参数 1 没有从 'A' 到 'const B&' 的已知转换

标签: c++ constructor operator-keyword


【解决方案1】:

你有一个隐式构造函数

B::B(const A&)

执行您不想要的转换。您可以将签名更改为

explicit B(const A&);

触发编译错误。请注意,默认情况下使用一个参数explicit 标记构造函数是一种很好的做法(也有一个clang-tidy check),只是为了避免意外进行此类转换(恕我直言,如果构造函数可以用单个参数构造,默认情况下是explicit,具有隐含的能力)。

【讨论】:

    【解决方案2】:

    您拥有的不是复制构造函数,而是转换构造函数。调用转换构造函数是因为您可以将A 转换为B

    这样做:

    explicit B(const A& x)
    

    现在您将禁止从AB 的隐式转换。

    当你这样做时:

    bar = foo;
    

    编译器会寻找合理的操作,最多允许一次转换。它可以使用来自B 的复制分配,并且它知道它可以从A 中创建一个B(因为构造函数不是显式的),所以它默默地执行此操作。

    正如@lubgr 所说,clang-tidy 有一个检查这些的规则,这是part of the C++ core guidelines

    【讨论】:

    • 你是对的,这是一个转换库。但是为什么在分配 bar = foo 发生时调用它。这就是我要考虑的重点
    • 用正在发生的事情更新了我的答案。
    【解决方案3】:

    我预计会产生以下编译错误

    no match for 'operator=' ... note: no known conversion for argument 1 from 'A' to 'const B&'
    

    没有理由预计会出现这种错误,因为存在Aconst B&amp; 的已知转换。 B 有一个 A 的转换构造函数:

    B(const A& x)
    

    它们之间的唯一联系是在 B 中我使用了一个将 B 转换为 A 的运算符

    ...它们之间的第二个联系是在B 中,您使用了将A 转换为B 的构造函数。

    你说得对,这是一个转换变量。但是为什么在分配 bar = foo 发生时调用它。

    因为赋值的给定操作数的类型是A,而声明的参数类型是const B&amp;

    如果参数类型与声明不匹配,编译器会检查是否存在可用于转换参数的隐式转换序列。由于A 可以隐式转换为B,所以存在这样的隐式转换序列并将被使用。

    【讨论】:

      猜你喜欢
      • 2021-05-14
      • 2021-02-18
      • 2014-01-14
      • 1970-01-01
      • 1970-01-01
      • 2012-02-28
      • 2011-04-18
      相关资源
      最近更新 更多