Когда вы ссылаетесь на переменную по имени, вы всегда получаете lvalue. Исключений из этого правила нет, хотя обратите внимание, что это не относится к препроцессорным макросам, счетчикам или параметрам шаблона непигового типа, ни одна из которых не является переменными в обычном смысле.
Я утверждаю, что хотя это поведение сначала кажется не имеющим смысла, когда вы считаете его более осторожным, оно имеет смысл и является правильным поведением. Во-первых, следует заметить, что категория значений явно является свойством выражений , , а не самих объектов. Это очевидно, так как std::move
никогда не создает новый объект, а просто создает выражение rvalue, относящееся к данному объекту. Тогда мы должны понимать, что:
T{}
в одном выражении отличается от T{}
в более позднем выражении, оба создают анонимные объекты, но оба они различны, поэтому последний не имеет доступа к тому же объекту, что и первый.) Таким образом, категория значений выражения, ссылающегося на объект, является relative; зависит от конкретного выражения и области видимости. std::move
сигнализирует о своем намерении не получать доступ к значению объекта снова в той же области действия, что позволяет вызываемой функции перемещать свое значение из этого объекта. Однако, когда вызываемая функция обращается к имени ссылки rvalue, это значение является постоянным во время вызова функции; функция может перемещать значение из этого объекта в любой точке или вообще не работать, но, во всяком случае, он, вероятно, получит доступ к нему в теле, который после инициализации параметров.
В этом примере :
void f(Foo&& foo) { /* use foo */ }
void g() {
Foo foo;
f(std::move(foo));
}
, хотя std::move(foo)
в g
и параметр foo
в вызываемой ссылке относятся к одному и тому же объекту, это значение объекта исчезнет на точки std::move
в g
, тогда как в f
ожидается, что значение этого объекта будет доступно через foo
, возможно, несколько раз до конца f
.
A аналогичная ситуация существует при вызове ref-квалифицированных функций-членов.
struct Foo {
void f() &;
void f() &&;
void g() && {
f(); // calls lvalue-qualified f
}
};
void h() {
Foo().g();
}
Здесь значение Foo()
вот-вот исчезнет из h()
; он не будет доступен после полного выражения . Однако в теле Foo::g()
он остается постоянным до конца g()
; *this
надежно обращается к значению одного и того же объекта. Поэтому естественно, что когда g()
вызывает f()
, он должен вызывать перегрузку, ожидающую lvalue, и f()
не должен красть значение *this*
из g()
, так как g()
может все еще хотеть получить к нему доступ .