Почему неправильно иметь многострочные функции constexpr?

В Java все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не самими объектами.

При попытке выполнить один метод объекта , ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, нуль, void, nada), то нет способа, которым метод будет выполнен. Тогда runtime сообщит вам об этом, выбросив исключение NullPointerException.

Ваша ссылка «указывает» на нуль, таким образом, «Null -> Pointer».

Объект живет в памяти виртуальной машины пространство и единственный способ доступа к нему - использовать ссылки this. Возьмем этот пример:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

И в другом месте вашего кода:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

Это важно знать - когда больше нет ссылок на объект (в пример выше, когда reference и otherReference оба указывают на null), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сбору мусора, и в какой-то момент VM освободит память, используемую этим объектом, и выделит другую.

25
задан Peter Alexander 12 July 2010 в 07:58
поделиться

5 ответов

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

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

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

Вкратце, это:

7 * 2 + 4 * 3

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

   +
  /\
 /  \
 *   *
/\  /\
7 2 4 3

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

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

int i0 = 7;
int i1 = 2;
int i2 = 4;
int i3 = 3;

int i4 = i0 * i1;
int i5 = i2 * i3;
int i6 = i4 + i5;
return i6;

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

Даже без всех мерзких «а что, если» код, который компилятор должен интерпретировать, стал намного более сложным. Синтаксическое дерево теперь может выглядеть примерно так: ( LD и ST - это операции загрузки и сохранения соответственно)

    ;    
    /\
   ST \
   /\  \
  i0 3  \
        ;
       /\
      ST \
      /\  \
     i1 4  \
           ;
          /\
         ST \
         / \ \
       i2  2  \
              ;
             /\
            ST \
            /\  \
           i3 7  \
                 ;
                /\
               ST \
               /\  \
              i4 *  \
                 /\  \
               LD LD  \
                |  |   \
                i0 i1   \
                        ;
                       /\
                      ST \
                      /\  \
                     i5 *  \
                        /\  \
                       LD LD \
                        |  |  \
                        i2 i3  \
                               ;
                              /\
                             ST \
                             /\  \
                            i6 +  \
                               /\  \
                              LD LD \
                               |  |  \
                               i4 i5  \
                                      LD
                                       |
                                       i6

Мало того, что оно выглядит намного больше сложный, теперь он также требует состояния. Раньше каждое поддерево можно было интерпретировать изолированно. Теперь все они зависят от остальной части программы.Одна из листовых операций LD не имеет смысла , если она не помещена в дерево так, чтобы операция ST выполнялась в том же месте ранее .

18
ответ дан 28 November 2019 в 21:45
поделиться

РЕДАКТИРОВАТЬ: игнорировать этот ответ. Ссылочная статья устарела. Стандарт допускает ограниченную рекурсию (см. Комментарии).

Обе формы являются незаконными. Рекурсия не разрешена в функциях constexpr из-за ограничения, что функция constexpr не может быть вызвана, пока она не определена. Ссылка, предоставленная OP, утверждает это в явном виде:

constexpr int twice(int x);
enum { bufsz = twice(256) }; // error: twice() isn’t (yet) defined

constexpr int fac(int x)
{ return x > 2 ? x * fac(x - 1) : 1; } // error: fac() not defined
                                       // before use

Несколько строчек ниже:

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

...

Мы (до сих пор) запрещаем рекурсию во всех ее формах в константных выражениях.

Без этих ограничений вы впутываетесь в проблему остановки (спасибо @Grant за потрясение моей памятью с вашим комментарием к моему другому ответу). Вместо того, чтобы устанавливать произвольные пределы рекурсии, дизайнеры посчитали более простым просто сказать «Нет».

2
ответ дан Marcelo Cantos 12 July 2010 в 07:58
поделиться

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

2
ответ дан 28 November 2019 в 21:45
поделиться

Вероятно, плохо сформирован, потому что его слишком сложно реализовать. Аналогичное решение было принято в первой версии стандарта в отношении закрытия функций-членов (т. Е. Возможности передать obj.func как вызываемую функцию). Возможно, более поздняя версия стандарта предоставит больше возможностей.

0
ответ дан 28 November 2019 в 21:45
поделиться

На случай, если здесь возникнет какая-то путаница, вы знаете, что функции / выражения constexpr вычисляются во время времени компиляции . Здесь нет проблем с производительностью во время выполнения.

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

Однако меня беспокоят проблемы с QoI. Интересно, будут ли разработчики компилятора достаточно умны, чтобы выполнить мемоизацию?

constexpr fib(int n) { return < 2 ? 1 : fib(n-1) + fib(n-2); }

Без мемоизации вышеуказанная функция имеет сложность O (2 n ) , что, конечно, не то, что я ' хочу пощупать, даже во время компиляции.

5
ответ дан 28 November 2019 в 21:45
поделиться
Другие вопросы по тегам:

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