使用 std::move 时运动构造函数与其他非运动构造函数发生冲突导致 C++ 编译错误
C++ compiling error due to a conflict between the movement constructor and other non-movement one when using std::move
我正在用移动语义做一些 "experiments" 以便完全理解它。我已经到了我完全不明白的地步。这是因为在使用std::move
的时候,好像和A::A(B b)
、A::A(B &&b)
这样的构造函数有冲突。下面是对此进行解释的代码:
struct Item {
Item() { }
Item(const Item &other) {
std::cout << "Item::Item(const Item& )\n";
}
};
struct Container {
Container(Item i) {
std::cout << "Container::Container(Item )\n";
}
Container(Item &&i) {
std::cout << "Container::Container(Item&& )\n";
}
};
int main() {
Item i;
Container c(std::move(i));
return 0;
}
尝试编译以上代码时,出现以下错误:
error C2668: 'Container::Container' : ambiguous call to overloaded function
1> c:\users\xe74139\documents\visual studio 2012\projects\project1\project1\maincpp.cpp(21): could be 'Container::Container(Item &&)'
1> c:\users\xe74139\documents\visual studio 2012\projects\project1\project1\maincpp.cpp(17): or 'Container::Container(Item)'
我知道构造函数 Container(Item i)
根本没有意义,但我仍然不明白为什么 C++ 编译器会发现两个 Container
构造函数之间存在冲突。
为什么不能确定Container c(i)
是调用Container(Item)
,Container c(std::move(i))
是调用Container(Item &&)
?
编辑: 或者,换句话说,为什么构造函数 Container(Item)
对 Container c(std::move(i))
?
这样的调用有效
据我所知,这是因为参数先求值,然后通过。一旦通过,它就是任何 r-value
,并且您的两个构造函数都可以工作。我不确定是否可能有这样的过载,从我的角度来看这没有意义。
为什么不呢?好吧,您正在尝试定义一个强制元素移动的容器。从设计的角度来看,您必须考虑到使用该代码的人可能不想要数据 "stolen"(已移动)。我认为在这种情况下更有意义:
struct Item {
Item() { }
Item(const Item &other) {
std::cout << "Item::Item(const Item& )\n";
}
Item(Item &&other) {
std::cout << "Item::Item(Item&&)\n";
}
};
此重载更有意义 - 您将明确说明何时移动和复制对象(此处有默认 move
,但这种方式使其更明显)。现在你只需要
Container(Item i);
(没有其他构造函数)并用
调用它
Container c(std::move(i));
是我们所期望的 - 而不是复制 i
,您明确表示您希望它移动,i
负责处理这个问题。
Why the constructor Container(Item) is valid for a call like Container c(std::move(i))?
因为当给定 std::move(i)
(这是一个 Item&&
)的结果时,您可以构造一个 Item
。
就像 HowardHinnant 在评论中所说的那样 - Item
和 Item&
是一样的 - 你可以在传递另一个时构造一个,所以如果你定义了这两个构造函数,你就已经造成歧义。
我正在用移动语义做一些 "experiments" 以便完全理解它。我已经到了我完全不明白的地步。这是因为在使用std::move
的时候,好像和A::A(B b)
、A::A(B &&b)
这样的构造函数有冲突。下面是对此进行解释的代码:
struct Item {
Item() { }
Item(const Item &other) {
std::cout << "Item::Item(const Item& )\n";
}
};
struct Container {
Container(Item i) {
std::cout << "Container::Container(Item )\n";
}
Container(Item &&i) {
std::cout << "Container::Container(Item&& )\n";
}
};
int main() {
Item i;
Container c(std::move(i));
return 0;
}
尝试编译以上代码时,出现以下错误:
error C2668: 'Container::Container' : ambiguous call to overloaded function
1> c:\users\xe74139\documents\visual studio 2012\projects\project1\project1\maincpp.cpp(21): could be 'Container::Container(Item &&)'
1> c:\users\xe74139\documents\visual studio 2012\projects\project1\project1\maincpp.cpp(17): or 'Container::Container(Item)'
我知道构造函数 Container(Item i)
根本没有意义,但我仍然不明白为什么 C++ 编译器会发现两个 Container
构造函数之间存在冲突。
为什么不能确定Container c(i)
是调用Container(Item)
,Container c(std::move(i))
是调用Container(Item &&)
?
编辑: 或者,换句话说,为什么构造函数 Container(Item)
对 Container c(std::move(i))
?
据我所知,这是因为参数先求值,然后通过。一旦通过,它就是任何 r-value
,并且您的两个构造函数都可以工作。我不确定是否可能有这样的过载,从我的角度来看这没有意义。
为什么不呢?好吧,您正在尝试定义一个强制元素移动的容器。从设计的角度来看,您必须考虑到使用该代码的人可能不想要数据 "stolen"(已移动)。我认为在这种情况下更有意义:
struct Item {
Item() { }
Item(const Item &other) {
std::cout << "Item::Item(const Item& )\n";
}
Item(Item &&other) {
std::cout << "Item::Item(Item&&)\n";
}
};
此重载更有意义 - 您将明确说明何时移动和复制对象(此处有默认 move
,但这种方式使其更明显)。现在你只需要
Container(Item i);
(没有其他构造函数)并用
调用它Container c(std::move(i));
是我们所期望的 - 而不是复制 i
,您明确表示您希望它移动,i
负责处理这个问题。
Why the constructor Container(Item) is valid for a call like Container c(std::move(i))?
因为当给定 std::move(i)
(这是一个 Item&&
)的结果时,您可以构造一个 Item
。
就像 HowardHinnant 在评论中所说的那样 - Item
和 Item&
是一样的 - 你可以在传递另一个时构造一个,所以如果你定义了这两个构造函数,你就已经造成歧义。