Почему ++, я рассмотрел l-значение, но я ++ не?

Ошибка анализа: синтаксическая ошибка, неожиданная '['

Эта ошибка возникает в двух вариантах:

Вариант 1

$arr = [1, 2, 3];

Синтаксис инициализатора этого массива был введен только в PHP 5.4; это приведет к возникновению ошибки парсера в версиях до этого. Если возможно, обновите свою установку или используйте старый синтаксис:

$arr = array(1, 2, 3);

См. Также этот пример из руководства.

Вариант 2

$suffix = explode(',', 'foo,bar')[1];

Результаты функции разыменования массива также были введены в PHP 5.4. Если обновление невозможно, вам нужно использовать временную переменную:

$parts = explode(',', 'foo,bar');
$suffix = $parts[1];

См. Также этот пример из руководства.

40
задан 19 revs, 8 users 46% 20 January 2018 в 02:05
поделиться

10 ответов

Хорошо как другая отвечающая сторона, на которую указывают уже, причина, почему ++i lvalue, состоит в том, чтобы передать его ссылке.

int v = 0;
int const & rcv = ++v; // would work if ++v is an rvalue too
int & rv = ++v; // would not work if ++v is an rvalue

причина второго правила состоит в том, чтобы позволить инициализировать ссылку с помощью литерала, когда ссылка является ссылкой на константу:

void taking_refc(int const& v);
taking_refc(10); // valid, 10 is an rvalue though!

, Почему мы представляем rvalue вообще, можно ли спросить. Ну, эти условия подходят при создании правил языка для этих двух ситуаций:

  • Мы хотим иметь значение локатора. Это представит местоположение, которое содержит значение, которое может быть считано.
  • Мы хотим представить значение выражения.

вышеупомянутые две точки взяты из Стандарта C99, который включает эту хорошую довольно полезную сноску:

[Название ‘‘lvalue’’ происходит первоначально от выражения присваивания E1 = E2, в котором левый операнд E1 требуется, чтобы быть (modiп¬Ѓable) lvalue. Это, возможно, лучше рассматривают как представление объекта ‘‘locator value’’. Что иногда называют, ‘‘rvalue’’ находится в этом Международном стандарте, описанном как ‘‘value expression’’.]

значение локатора называют lvalue, в то время как значение, следующее из оценки того местоположения, называют rvalue. Правильно соответственно также к Стандарту C++ (говорящий о lvalue-to-rvalue преобразовании):

4.1/2: значение, содержавшееся в объекте, обозначенном lvalue, является результатом rvalue.

Заключение

Используя вышеупомянутую семантику, ясно теперь, почему i++ не lvalue, но rvalue. Поскольку возвращенное выражение не расположено в i больше (оно увеличено!), это - просто значение, которое может представлять интерес. Изменение того значения, возвращенного i++, сделало бы не, распознаются, потому что мы не имеем расположения, из которого мы могли считать то значение снова. И таким образом, в Стандарте говорится, что это - rvalue, и это таким образом может только связать со ссылкой на константу.

Однако в constrast, выражение, возвращенное ++i, является местоположением (lvalue) i. Вызов lvalue-to-rvalue преобразования, как в int a = ++i; считает значение из него. С другой стороны, мы можем высказать контрольное мнение к нему и считать значение позже: int &a = ++i;.

Примечание также другие случаи, где rvalues сгенерированы. Например, все временные файлы являются rvalues, результатом двоичных/унарных + и минус и все выражения возвращаемого значения, которые не являются ссылками. Все те выражения не расположены в именованном объекте, но несут скорее значения только. Те значения могут, конечно, быть сохранены объектами, которые не являются постоянными.

следующая Версия C++ будет включать так называемый rvalue references, что, даже при том, что они указывают на nonconst, может связать с rvalue. Объяснение состоит в том, чтобы смочь "украсть" далеко ресурсы из тех анонимных объектов и избежать копий, делающих это. При принятии типа класса, который перегрузил префикс ++ (возврат Object&) и постфикс ++ (возврат Object), следующее вызвало бы копию сначала, и для второго случая это украдет ресурсы из rvalue:

Object o1(++a); // lvalue => can't steal. It will deep copy.
Object o2(a++); // rvalue => steal resources (like just swapping pointers)
25
ответ дан 2 revs 27 November 2019 в 01:37
поделиться

Другие люди занялись функциональным различием между сообщением и пред инкремент.

До того, чтобы быть lvalue затронут, i++ не может быть присвоен тому, потому что он не относится к переменной. Это относится к расчетному значению.

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

i++   = 5;
i + 0 = 5;

, поскольку преинкремент возвращает ссылку на увеличенную переменную, а не временную копию, ++i, lvalue.

Предпочтение преинкремента по причинам производительности становится особенно хорошей идеей при постепенном увеличении чего-то как объект итератора (например, в STL), который может быть хорошим битом больше тяжеловеса, чем интервал

32
ответ дан mackenir 27 November 2019 в 01:37
поделиться

Кажется, что много людей объясняет, как ++i lvalue, но не , почему , как в, , почему сделал комитет по стандартам C++, вставляет эту функцию, особенно в свете того факта, что C не позволяет ни одному как lvalues. От это обсуждение comp.std.c ++ , кажется, что это так, можно взять его адрес или присвоить ссылке. Пример кода извлек из сообщения Christian Bau:

   int i;
   extern void f (int* p);
   extern void g (int& p);

   f (&++i);   /* Would be illegal C, but C programmers
                  havent missed this feature */
   g (++i);    /* C++ programmers would like this to be legal */
   g (i++);    /* Not legal C++, and it would be difficult to
                  give this meaningful semantics */

Между прочим, если i, оказывается, встроенный тип, то операторы присваивания такой как ++i = 10 вызывают неопределенное поведение , потому что i изменяется дважды между точками последовательности.

10
ответ дан Nietzche-jou 27 November 2019 в 01:37
поделиться

Я получаю lvalue ошибку, когда я пытаюсь скомпилировать

i++ = 2;

, но не, когда я изменяю ее на

++i = 2;

, Это вызвано тем, что префиксный оператор (++ i) изменяет значение во мне, затем возвращается i, таким образом, ей можно все еще присвоить. Постфиксный оператор (я ++) изменяет значение во мне, но возвращает временную копию старого значение , который не может быть изменен оператором присваивания.

<час>

Ответ на исходный вопрос :

, Если Вы говорите об использовании инкрементных операторов в операторе собой, как в для цикла, это действительно не имеет никакого значения. Преинкремент, кажется, более эффективен, потому что постинкремент должен увеличить себя и возвратить временную ценность, но компилятор оптимизирует это различие далеко.

for(int i=0; i<limit; i++)
...

совпадает с

for(int i=0; i<limit; ++i)
...

, Вещи становятся немного более сложными при использовании возвращаемого значения операции как часть большего оператора.

Даже два простых оператора

int i = 0;
int a = i++;

и

int i = 0;
int a = ++i;

отличаются. То, которые увеличивают оператор, который Вы принимаете решение использовать в качестве части операторов мультиоператора, зависит от того, каково намеченное поведение. Короче говоря, нет Вы не можете только выбрать тот. Необходимо понять обоих.

5
ответ дан Bill the Lizard 27 November 2019 в 01:37
поделиться

POD Пред инкремент:

преинкремент должен действовать, как будто объект был увеличен перед выражением и быть применимым в этом выражении, как будто это произошло. Таким образом комитет стандартов C++ решил, что может также использоваться в качестве l-значения.

инкремент Сообщения POD:

постинкремент должен увеличить объект POD и возвратить копию для использования в выражении (См. Раздел n2521 5.2.6). Поскольку копия не является на самом деле переменной, делающей его, l-значение не имеет никакого смысла.

Объекты:

Пред и инкремент Сообщения на объектах просто синтаксический сахар языка, обеспечивает средство назвать методы на объекте. Таким образом технически Объекты не ограничиваются стандартным поведением языка, но только ограничениями, введенными вызовами метода.

Это до конструктора этих методов, чтобы заставить поведение этих объектов зеркально отразить поведение объектов POD (Это не требуется, но ожидается).

Преинкремент Объектов:

требование (ожидаемое поведение) вот - то, что объекты увеличены (значение иждивенца на объекте), и метод возвращают значение, которое является модифицируемым и является похожим на исходный объект после того, как инкремент произошел (как будто инкремент произошел перед этим оператором).

, Чтобы сделать это - siple, и только потребуйте, чтобы метод возвратил ссылку на себя. Ссылка является l-значением и таким образом будет вести себя как ожидалось.

Постинкремент Объектов:

требование (ожидаемое поведение) вот - то, что объект увеличен (таким же образом как преинкремент), и возвращенное значение похоже на старое значение и неизменяемо (так, чтобы это не вело себя как l-значение).

Неизменяемый:
, Чтобы сделать это необходимо возвратить объект. Если объект будет использоваться в рамках выражения, то это будет копия, созданная во временную переменную. Временные переменные являются константой, и таким образом она будет неизменяемый и вести себя как ожидалось.

Похож на старое значение:
Это просто достигается путем создания копии оригинала (вероятно, использование конструктора копии) прежде makeing любые модификации. Копия должна быть глубокой копией иначе, любые изменения в оригинале будут влиять на копию, и таким образом состояние изменится в отношениях к выражению с помощью объекта.

Таким же образом как преинкремент:
, вероятно, лучше реализовать инкремент сообщения с точки зрения преинкремента так, чтобы Вы получили то же поведение.

class Node // Simple Example
{
     /*
      * Pre-Increment:
      * To make the result non-mutable return an object
      */
     Node operator++(int)
     {
         Node result(*this);   // Make a copy
         operator++();         // Define Post increment in terms of Pre-Increment

         return result;        // return the copy (which looks like the original)
     }

     /*
      * Post-Increment:
      * To make the result an l-value return a reference to this object
      */
     Node& operator++()
     {
         /*
          * Update the state appropriatetly */
         return *this;
     }
};
4
ответ дан 7 revs 27 November 2019 в 01:37
поделиться

Относительно LValue

  • В C (и Perl, например), ни ++i, ни i++ LValues.

  • В C++, i++ не, и LValue, но ++i.

    ++i эквивалентно i += 1, который эквивалентен [1 110].
    результат состоит в том, что мы все еще имеем дело с тем же объектом i.
    Это может быть просмотрено как:

    int i = 0;
    ++i = 3;  
    // is understood as
    i = i + 1;  // i now equals 1
    i = 3;
    

    i++, с другой стороны, мог быть просмотрен как:
    Первый мы используем значение из [1 113], затем увеличиваем объект i.

    int i = 0;
    i++ = 3;  
    // would be understood as 
    0 = 3  // Wrong!
    i = i + 1;
    

(редактирование: обновленный после покрытой пятнами первой попытки).

2
ответ дан Renaud Bompuis 27 November 2019 в 01:37
поделиться

Основное различие - то, что я ++ возвращаю преинкрементное значение, тогда как ++ я возвращаю постинкрементное значение. Я обычно использую ++ я, если у меня нет очень неопровержимого довода для использования i ++ - а именно, если я действительно делаю , нуждаются в преинкрементном значении.

, по моему скромному мнению, это - хорошая практика для использования, '++ я' формируюсь. В то время как различие между пред - и постинкремент не действительно измеримо при сравнении целых чисел или других ПЕРЕХОДНЫХ ПРИСТАВОК дополнительная объектная копия необходимо сделать и возвратиться при использовании, 'я ++' могу представить значительное влияние производительности, если объект является или довольно дорогим для копирования, или увеличиваемый часто.

0
ответ дан Timo Geusch 27 November 2019 в 01:37
поделиться

Между прочим - избегают использования нескольких инкрементных операторов на той же переменной в том же операторе. Вы попадаете в беду, "где точки последовательности" и неопределенный порядок операций, по крайней мере, в C. Я думаю, что часть этого была очищена в Java без обозначения даты C#.

0
ответ дан Paul Tomblin 27 November 2019 в 01:37
поделиться

Возможно, это имеет некоторое отношение к способу, которым реализован постинкремент. Возможно, это - что-то вроде этого:

  • Создают копию исходного значения в памяти
  • Инкремент исходная переменная
  • Возврат копия

, Так как копия не является ни переменной, ни ссылкой на динамично выделенную память, это не может быть l-значение.

0
ответ дан pyon 27 November 2019 в 01:37
поделиться

C#:

public void test(int n)
{
  Console.WriteLine(n++);
  Console.WriteLine(++n);
}

/* Output:
n
n+2
*/
-5
ответ дан LeppyR64 27 November 2019 в 01:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: