Правда ли, что переменные, адрес которых может быть взят, являются 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 имеет расширения, которые позволяют вам взять адрес prvalues, и GCC позволяет вам включить этот вид расширения, используя -fpermissive:

Понизьте некоторую диагностику несоответствующего кода с ошибок до предупреждений. Таким образом, использование -fpermissive позволит компилировать некоторый несоответствующий код.

Это означает, что Скотт Мейерс прав, а компиляторы не правы, по крайней мере, в вопросе соответствия стандарту. Также мимоходом -fpermissive вы сказали, что gcc должен быть менее строгим, т. е. разрешать компиляцию вашего несоответствующего кода. Чтобы полностью соответствовать, вы всегда должны компилировать с -pedantic или же -pedantic-errors (потому что, как и MSVC, gcc также включает некоторые языковые расширения по умолчанию).

Основываясь на комментариях Холта и поскольку я отредактировал вопрос, я думаю, что правильный ответ должен быть Test(), в приведенном выше контексте это rvalue, и если мы используем стандартизированный компилятор, этот код не будет компилироваться, поэтому, если ваш код компилируется, это означает либо флаг fpermissive установлен в gcc или с msvc, ваши настройки позволяют вам работать. Вторая строка Test() = Test() работает, потому что она эквивалентна Test(). Operator=() и мы можем вызывать функцию из rvalue. Теперь третья строка является неявной, поскольку она является значением r, поэтому мы можем иметь ссылку на это значение.

Следовательно, с помощью этого примера это доказывает, что мы можем получить адрес lvalue только при условии, что мы используем стандартизированный компилятор.

Вся заслуга в этом вопросе должна состояться, поскольку я только что написал ответ, так как люди обычно не читают комментарии, а также потому, что я редактировал вопрос

Другие вопросы по тегам