Имеется список типов, в котором перечисляется. что может являться lvalue, а что rvalue. В этом списке написано, что тип Строковый литерал является lvalue:
l-value:
- Выражения, непосредственно обозначающие объект, non-modifiable в случае const-квалификации. Например, имя переменной, параметра функции и т.п.
- Выражения ссылочных типов. non-modifiable в случае const-квалификации. В частности, результат вызова функций, возвращающих объекты по ссылке; выражения, состоящие из имен ссылочных переменных; операции преобразования к ссылочному типу и т.д.
- Результат встроенной операции разыменования (*) — lvalue указуемого типа; non-modifiable в случае const-квалификации.
- Результат встроенных префиксных операций ++, --.
- Имя функции.
- Имя массива.
- Строковые литералы. - Вот это что за?
r-value:
- Выражения, обозначающие временные объекты. В частности, результат вызова функций, возвращающих объекты не по ссылке; результат встроенных операций +, -. > *, / и т.п.; явное создание временной переменной int() или C(); преобразования не к ссылочным типам и т.д.
- Результат встроенной операции взятия адреса (&) — rvalue типа указатель.
- Результат встроенных постфиксных операций ++, --.
- Литералы, за исключением строковых (например, числовые литералы).
- Константы перечислений.
Возникает вопрос, а почему строковый литерал является l-value? Ведь нельзя написать так:
"This is str-literal" = "Any value";
А дело в том, что термины l-value и r-value по историческим причинам не отражают того, чем они на самом деле являются. l-value - это не просто "нечто, что стоит слева от знака присвоения". То же самое касается и r-value.
Более точным определением l-value будет такое: l-value - это некий объект в памяти, от которого можно взять адрес.
Обычно для того, чтобы произошло присвоение через память, надо знать, куда в памяти в конечном итоге будет положено присваимоемое значение. И именно поэтому, чтобы присвоение состоялось, присвоение должно происходить с такой сущностью, у которой адрес имеется.
С другой стороны, строка - это такая сущность, которая всегда размещена в памяти, и у нее есть адрес начала строки. А коль у нее есть адрес, значит таковой объект является l-value. В этом и состоит ответ на вопрос, заданный в заголовке.
Подытоживая, можно сказать: принадлежность к l-value совершенно не значит, что такой объект может стоять слева от знака присвоения, и ему может быть таким образом присвоено значение. Принадлежность к l-value означает, что у объекта имеется адрес, и не более того.
Что касается вышеприведенного кода присвоения строки, то он не будет компилироваться потому, что синтаксический анализатор языка C++ запрещает использование таких конструкций. Причем он опирается не на тупую эвристику "слева от знака равно должно быть l-value", а на другие более обширные правила.
Тут возникает вопрос: а как выглядит сущность, которая не имеет адреса? То есть, как выглядит r-value? Вот примеры:
- Какое-то число (числовой литерал). Ведь нальзя написать так:
250=a+b;
- Результат вычисления математического выражения. Можно ли что-то присвоить выражению (a+100), например так:
(a+100)=500;
нет, нельзя.
- Результат вызова функции. То же самое, нельзя написать:
sin(alfa)=1.5;
- Взятие адреса переменной. Ведь нельзя взять адрес переменной, а потом сказать - при получении адреса у меня получился другой адрес!
&variableName=0x123456789
Во всех этих примерах слева от знака равно написана сущность, которая может не иметь адреса. А если может не иметь адреса, значит это r-value. Не очень точно, но можно сказать, что результату работы выражения r-value может не потребоваться память, например потому что результат может быть размещен в регистре. То есть, у такой сущности невозможно получить адрес в памяти.
Примерно так следует воспринимать разницу между выражениями l-value и r-value.