Правда ли, что переменные, адрес которых может быть взят, являются lvalues?
Недавно, читая блог Скотта Мейерса об универсальной ссылке, я пришел с утверждением, что "если вы можете взять адрес выражения, выражение будет lvalue". но действительно ли это правда
Предположим, у меня есть следующий код
class Test
{
};
int main()
{
std::cout << "Address is " << &(Test()) << std::endl;
Test() = Test();
Test&& t = Test();
return 0;
}
В вышеприведенном случае Test() является временным, то есть rvalue, и я могу получить адрес этого (с gcc мы можем использовать -fpremissive, а с msvc он будет компилироваться напрямую), так что с этим мы можем сказать, что это lvalue, а также bcoz мы можем сделать Test() = Test(), но так как мы можем взять ссылку на rvalue, она должна быть rvalue.
Так почему же мы всегда говорим, что если мы можем взять адрес переменной, то это lvalue?
2 ответа
Ваш первый код неверен, потому что объявление z
должно быть int*
этот код компилируется и работает нормально:
int x = 12; // x is lvalue
int& y = x; // y is lvalue reference
int *z = &y; // can take address of lvalue reference (address of the referenced)
assert(z == &x);
Для вашего второго примера Test()
на самом деле prvalue
и вы не можете взять его адрес в переносном виде, как указано в стандарте.
MSVC имеет расширения, которые позволяют вам взять адрес prvalue
s, и GCC позволяет вам включить этот вид расширения, используя -fpermissive
:
Понизьте некоторую диагностику несоответствующего кода с ошибок до предупреждений. Таким образом, использование -fpermissive позволит компилировать некоторый несоответствующий код.
Это означает, что Скотт Мейерс прав, а компиляторы не правы, по крайней мере, в вопросе соответствия стандарту. Также мимоходом -fpermissive
вы сказали, что gcc должен быть менее строгим, т. е. разрешать компиляцию вашего несоответствующего кода. Чтобы полностью соответствовать, вы всегда должны компилировать с -pedantic
или же -pedantic-errors
(потому что, как и MSVC, gcc также включает некоторые языковые расширения по умолчанию).
Основываясь на комментариях Холта и поскольку я отредактировал вопрос, я думаю, что правильный ответ должен быть Test(), в приведенном выше контексте это rvalue, и если мы используем стандартизированный компилятор, этот код не будет компилироваться, поэтому, если ваш код компилируется, это означает либо флаг fpermissive установлен в gcc или с msvc, ваши настройки позволяют вам работать. Вторая строка Test() = Test() работает, потому что она эквивалентна Test(). Operator=() и мы можем вызывать функцию из rvalue. Теперь третья строка является неявной, поскольку она является значением r, поэтому мы можем иметь ссылку на это значение.
Следовательно, с помощью этого примера это доказывает, что мы можем получить адрес lvalue только при условии, что мы используем стандартизированный компилятор.
Вся заслуга в этом вопросе должна состояться, поскольку я только что написал ответ, так как люди обычно не читают комментарии, а также потому, что я редактировал вопрос