Что представляет собой адрес ссылки lvalue на prvalue?
Когда параметр функции имеет тип lvalue ссылка lref
:
void PrintAddress(const std::string& lref) {
std::cout << &lref << std::endl;
}
а также lref
связан с prvalue:
PrintAddress(lref.substr() /* temporary of type std::string */)
что представляет собой адрес? Что там живет?
Prvalue не может иметь свой адрес. Но ссылка lvalue на prvalue может быть взята по его адресу, что мне любопытно.
3 ответа
Короче говоря, потому что срок службы prvalue был продлен. Если время его жизни увеличено - по любой ссылке - это lvalue, и, следовательно, можно получить его адрес.
что представляет собой адрес? Что там живет?
Адрес представляет объект, объект, на который ссылается lref
,
Prvalue недолгим, он не живет долго. Фактически, он будет уничтожен, когда оператор, создающий его, закончится.
Но когда вы создаете ссылку на prvalue (либо ссылку на rvalue, либо ссылку на const lvalue), время ее жизни увеличивается. Ref.::
Значение r может быть использовано для инициализации ссылки const lvalue [rvalue], и в этом случае время жизни объекта, идентифицируемого значением r, увеличивается до тех пор, пока не закончится область действия ссылки.
Теперь имеет смысл взять его адрес, так как это значение для всех намерений и целей. Теперь, когда prvalue имеет неопределенное время жизни, это lvalue.
Однако брать адрес prvalue не имеет смысла, и, вероятно, поэтому он запрещен:
- Значение уничтожается после следующих операторов, так что вы ничего не можете сделать с адресом, кроме, может быть, распечатать его.
Если вы берете адрес чего-либо, компилятор должен фактически создать объект. Иногда компилятор оптимизирует тривиальные переменные, но если вы взяли их адрес, компилятору не будет разрешено их оптимизировать.
Таким образом, получение адреса prvalue приведет к тому, что компилятор не сможет полностью исключить значение без каких-либо преимуществ (см. Пункт 1).
Внутри функции lref
это не prvalue, это lvalue, и вы можете взять его адрес.
Существует распространенное заблуждение о rvalues против lvalues. Именованный параметр всегда является lvalue. Независимо от того, является ли это ссылочным типом, связанным с rvalue. Через const &
ссылочный тип, вы даже не можете сказать, какую категорию значений имеет объект на самом деле в точке, где вызывается функция. Ссылки Rvalue и неконстантные ссылки Lvalue дают вам эту информацию:
void foo(std::string& L, std::string&& R)
{
// yeah i know L is already an lvalue at the point where foo is called
// R on the other hand is an rvalue at the point where we get called
// so we can 'safely' move from it or something...
}
Временная строка является prvalue в контексте вызывающей стороны (в точке PrintAddress
называется). В контексте вызываемого абонента (в PrintAddress
) lref
является ссылкой на lvalue, потому что в этом контексте это фактически lvalue.
PrintAddress
не знает об ограниченном времени жизни переданного аргумента и от PrintAddress
С точки зрения объекта "всегда" там.
std::string q("abcd");
PrintAddress(q.substr(1)); // print address of temporary
концептуально эквивалентно:
std::string q("abcd");
{
const std::string& lref = q.substr(1);
std::cout << &lref << std::endl;
}
где временный испытывает продление его жизни до конца области, в которой lref
определяется (что до конца PrintAddress
область действия функции в настоящем примере).
что представляет собой адрес? Что там живет?
std::string
объект, содержащий переданный контент.
И является ли законным (в C++ и в отношении памяти) запись по этому адресу?
Нет, было бы законно, если бы вы использовали ссылку rvalue:
void PrintAddressR(std::string&& rref) {
rref += "Hello"; // writing possible
std::cout << &rref << std::endl; // taking the address possible
}
// ...
PrintAddressR(q.substr(1)); // yep, can do that...
То же самое относится и здесь: rref
это lvalue (у него есть имя), так что вы можете взять его адрес и он изменчив.
На простом английском:
void PrintAddress(const std::string& lref) {
std::cout << &lref << std::endl;
}
Любой объект, имеющий имя, является lvalue
следовательно, любое использование lref
в рамках вышеуказанной функции lvalue
использовать.
Когда вы вызвали функцию с помощью:
PrintAddress(lref.substr() /* temporary of type std::string */)
Конечно, lref.substr()
производит временный, который является rvalue
, но rvalues
может связываться с (время его продления увеличивается) ссылками const lvalue или ссылками rvalue.
Даже если вы предоставили rvalue
перегрузка, потому что у нее есть имя, это "значение чего-то" в своей области, например:
#include <string>
#include <iostream>
void PrintAddress(const std::string& lref) {
std::cout << "LValue: " << &lref << std::endl;
}
void PrintAddress(std::string&& `rref`) {
std::cout << "RValue: " << &rref << std::endl; //You can take address of `rref`
}
int main(){
std::string str = "Hahaha";
PrintAddress(str);
PrintAddress(str.substr(2));
}
Просто помни:
В C++ любой объект (будь то тип значения, ссылочный тип или тип указателя), имеющий имя, является lvalue
Также знайте, что некоторые выражения также производят lvalues.