更新 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
?
答案 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);