Я провожу «эксперименты» с семантикой перемещения, чтобы полностью ее понять. Я дошел до того, что не совсем понимаю. Это связано с тем, что кажется, что существует конфликт с конструктором, подобным A::A(B b)
и A::A(B &&b)
при использовании std::move
. Ниже приведен код, объясняющий это:
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))
?
2 ответа
Почему конструктор Container (Item) действителен для вызова типа Container c (std :: move (i))?
Потому что вы можете построить Item
, получив результат std::move(i)
(который является Item&&
).
И, как сказал Ховард Хиннант в комментарии - это то же самое для Item
vs Item&
- вы можете построить один, передав другой, поэтому, если у вас определены оба этих конструктора, вы создали двоякое выражение.
Насколько я могу судить, это потому, что аргумент сначала оценивается, а затем передается. Как только он будет принят, это будет любой r-value
, и оба ваших конструктора будут работать. Я не уверен, что возможна такая перегрузка, и с моей точки зрения это не имеет смысла.
Почему нет? Что ж, вы пытаетесь определить контейнер, который заставляет элемент перемещаться. С точки зрения дизайна, вы должны учитывать, что кто-то, использующий этот код, может не захотеть «украсть» (переместить) данные. Я думаю, что в этом контексте было бы разумнее иметь:
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
отвечает за это.
Похожие вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который должен быть) скомпилирован с помощью компилятора C ++. Используйте тег для конкретной версии для вопросов, связанных с конкретной версией стандарта [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] или [C ++ 23] и т. Д. .