Бесконечная компиляция с шаблонами

#c #templates #compiler-construction #recursion

#c #шаблоны #компилятор-конструирование #рекурсия

Вопрос:

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

 error: incomplete type ‘X<-0x000000000000001ca>’ used in nested name specifier
  

В некоторых случаях компиляция идет бесконечно. Например, смотрите приведенный ниже код (просто для иллюстрации; скомпилирован с помощью gcc 4.4.1):

 template<int I>
struct Infinite
{
  enum { value = (I amp; 0x1)? Infinite<I 1>::value : Infinite<I-1>::value };
};

int main ()
{
  int i = Infinite<1>::value;
}
  

Разве компилятор не должен быть достаточно умен, чтобы остановиться в какой-то момент?

Редактировать: Ошибка компиляции, показанная выше, относится к другому коду. В примере кода компиляция никогда не останавливается (однако я вижу такие ошибки в промежутках)

Комментарии:

1. Это заканчивается, по крайней мере, в g 4.8.4… main.cpp:4:27: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) instantiating ‘struct Infinite<-899>’

Ответ №1:

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

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

Однако я вполне могу ошибаться, что вы можете поместить любое вычисление в список параметров (но имитация регистровой машины, по-видимому, возможна, поскольку мы можем кодировать все регистры в отдельном целочисленном параметре шаблона (да, int он конечный, но довольно большой, что делает его практически неограниченным)).

Ответ №2:

Перевод синтаксического анализатора в бесконечный цикл с использованием шаблона не является чем-то новым.

 // Stresses the compiler infinitely
// from: http://www.fefe.de/c  /c++-talk.pdf
template<class T> struct Loop { Loop<T*> operator->(); };
Loop<int> i, j = i->hooray;
  

Ответ №3:

Компилятор выполняет то, что вы просите его сделать. Вы попросили его выполнить бесконечную рекурсию — он сделал именно это. Если вы хотите, чтобы это «остановилось в какой-то момент», вы должны попросить его остановиться в «какой-то момент» и сообщить ему, какое именно «какое-то время» вы точно имеете в виду.

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

Комментарии:

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

Ответ №4:

Разве компилятор не должен быть достаточно умен, чтобы остановиться в какой-то момент?

Как вы определяете фразу «в какой-то момент»? Откуда компилятору знать ваше определение «в какой-то момент»? Как он узнает, когда его нужно остановить, если вы не укажете это явно? Сначала вы должны сообщить об этом, определив специализацию (ы) шаблона непрерывного класса (то, что вы написали, является шаблоном непрерывного класса).

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

 template<>
struct Infinite<100> //this is to stop template with <I 1> argument
{
  enum { value = 678678 }; //see, it doesn't use Infinite<> any further!
};

template<>
struct Infinite<-100> //this is to stop template with <I-1> argument
{
  enum { value = -67878 }; //see, it too doesn't use Infinite<> any further!
};
  

Комментарии:

1. @Nawaz, на самом деле вопрос в большей перспективе компиляции. Почему компиляция не должна останавливаться в какой-то момент, когда становится известно, что компиляция продолжается бесконечно.

2. @iammilind: Откуда ему знать? Кроме того, в какой момент это должно прекратиться?

3. @iammilind: «Почему компиляция не должна останавливаться в какой-то момент, когда становится известно, что компиляция продолжается бесконечно». — Это действительно остановилось. В вашем случае это остановилось, когда выдало ошибку компиляции.

4. @iammilind: «Знаете, что компиляция будет бесконечной»? В общем случае «знать», что компиляция будет бесконечной, является чрезвычайно сложной (или даже неразрешимой) задачей. Компилятор не обязан заниматься подобными вопросами. Это проблема QoUI: если компилятор желает потратить свои усилия на отслеживание таких ситуаций, он может. Но никто этого не требует.

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