Это своего рода список инициализации в фигурных скобках. [dcl.init.list]/1.3
Чтобы быть еще более конкретным, это expr-or-braced-init-list [dcl.init]/1
оператора return [stmt.return]/2
Оператор return с любым другим операндом должен использоваться только в функции, тип возвращаемого значения которой не cv void; оператор return инициализирует объект результата glvalue или prvalue вызова функции (явного или неявного) путем инициализации копирования из операнда.
С этого момента позвольте мне процитировать ответ xskxzr, в котором упоминается [class.copy. исключение]/3
В следующих контекстах инициализации копирования операция перемещения может использоваться вместо операции копирования:
- Если выражение в операторе возврата ([stmt.return]) является (возможно, заключенным в скобки) id-выражением, которое называет объект с автоматическим сроком хранения, объявленным в теле или предложении-объявления-параметра самой внутренней охватывающей функции или лямбда-выражения , или же
Говоря обычными человеческими словами, причина, по которой вместо перемещения вызывается копирование, заключается в том, что в фигурном списке инициализации вызывается u
, который оказался lvalue.
Итак, вы можете знать, вызывает ли braced-init-list u
то есть rvalue...
return {std::move(u)};
Что ж, u
перемещается на новое значение rvalue UserName
, и сразу после этого срабатывает копирование.
Так что это занимает одно движение, как в
return u;
godbolt.org/g/b6stLr
wandbox.org/permlink/7u1cPc0TG9gqToZD
#include <iostream>
#include <optional>
struct UserName
{
int x;
UserName() : x(0) {};
UserName(const UserName& other) : x(other.x) { std::cout << "copy " << x << "\n"; };
UserName(UserName&& other) : x(other.x) { std::cout << "move " << x << "\n"; };
};
std::optional<UserName> CreateUser()
{
UserName u;
return u; // this one moves UserName
}
std::optional<UserName> CreateUser_listinit()
{
UserName u;
auto whatever{u};
return whatever;
}
std::optional<UserName> CreateUser_listinit_with_copy_elision()
{
UserName u;
return {u};
}
std::optional<UserName> CreateUser_move_listinit_with_copy_elision()
{
UserName u;
return {std::move(u)};
}
int main()
{
std::cout << "CreateUser() :\n";
[[maybe_unused]] auto d = CreateUser();
std::cout << "\nCreateUser_listinit() :\n";
[[maybe_unused]] auto e = CreateUser_listinit();
std::cout << "\nCreateUser_listinit_with_copy_elision() :\n";
[[maybe_unused]] auto f = CreateUser_listinit_with_copy_elision();
std::cout << "\nCreateUser_move_listinit_with_copy_elision() :\n";
[[maybe_unused]] auto g = CreateUser_move_listinit_with_copy_elision();
}
Распечатать
CreateUser() :
move 0
CreateUser_listinit() :
copy 0
move 0
CreateUser_listinit_with_copy_elision() :
copy 0
CreateUser_move_listinit_with_copy_elision() :
move 0
person
sandthorn
schedule
18.07.2018
std::unique_ptr<int> foo() { std::unique_ptr<int> p; return {p}; }
- person Slava   schedule 18.07.2018