隐式转换和复制构造函数

时间:2015-10-30 15:46:55

标签: c++

更新 suggested duplicate仅解决了此问题的部分内容。理解发生了什么的关键(首次创建临时参考的事实)在那里没有解释。

这是我第一次使用隐式转换,所以我写了这个:

class A {};

class B {
public:
    B(A& other) {}
    // Copy constructor
    B(const B& other) {}
};

int main() {
    A foo;
    B bar = foo;
}

这按预期编译,但是如果我删除const,我的编译器(gcc版本4.8.4)会在赋值时产生,并且出现错误消息,我无法理解:< / p>

test.cc: In function ‘int main()’:
test.cc:12:13: error: no matching function for call to ‘B::B(B)’
     B bar = foo;
             ^
test.cc:12:13: note: candidates are:
test.cc:7:5: note: B::B(B&)
     B(B& other) {}
     ^
test.cc:7:5: note:   no known conversion for argument 1 from ‘B’ to ‘B&’
test.cc:5:5: note: B::B(A&)
     B(A& other) {}
     ^
test.cc:5:5: note:   no known conversion for argument 1 from ‘B’ to ‘A&’

这是有效的C ++代码吗?当我尝试分配no matching function for call to ‘B::B(B)’时,为什么会说A

2 个答案:

答案 0 :(得分:7)

此声明

B bar = foo;

以下列方式工作:

首先,编译器使用构造函数创建一个临时对象:

B(A& other) {}

然后它尝试在复制构造函数中使用此临时对象:

B(B& other) {}

但是它可能不会使用非常量引用绑定临时对象,并且会发出错误。

当您使用等号时,则使用所谓的复制初始化。

如果你写了

B bar( foo );

然后这里将使用所谓的直接初始化,即不调用复制构造函数。在这种情况下,这段代码将被编译。

考虑到可以绕过复制/移动构造函数,并且可以直接在目标对象中构建临时对象。这称为 copy elision 。然而,所有规则都应保持,就像明确调用复制/移动构造函数一样。

例如,如果为类B的构造函数添加输出语句

class A {};

class B {
public:
    B(A& other) { std::cout << "B::B( A & )" << std::endl; }
    // Copy constructor
    B(const B& other) { std::cout << "B::B( const B & )" << std::endl; }
};

int main()
{
    A foo;
    B bar = foo;
}

然后你将看不到消息

B::B( const B & )

但是复制构造函数应该是可访问的。

例如,如果您将其设为私有

class A {};

class B {
public:
    B(A& other) { std::cout << "B::B( A & )" << std::endl; }
    // Copy constructor
private:
    B(const B& other) { std::cout << "B::B( const B & )" << std::endl; }
};

int main()
{
    A foo;
    B bar = foo;
}

程序不会编译(只有它不是MS VC ++编译器。:))

答案 1 :(得分:5)

B bar = foo被称为复制初始化:当右侧的类型与左侧的类型不匹配时(在这种情况下,B } vs A)编译器创建一个从右侧初始化的临时文件,然后使用copy-constructor复制它。这实际上就是你的代码:

B bar = B(foo);

由于您已从复制构造函数中删除const,因此您现在会收到错误,因为您正尝试将右值绑定到左值引用。正如您已经看到的那样,Rvalues可以绑定到const左值引用。

可以使用 direct-initialization 解决此问题。现在没有创建副本:

B bar(foo);