Подтвердить что ты не робот

Почему С++ никогда не разрешало использовать функции до их объявления?

Хорошо, я знаю, что это похоже на дубликат Почему функции должны быть объявлены до их использования?, но это не похоже на то, что существующие ответы полностью адресованы все детали.

Я знаю, что С++ изначально был разработан в 80, поэтому его можно было перевести за один проход, потому что компьютеры были медленными. ОК. Но самый последний стандарт был опубликован в 2011 году, поэтому я не понимаю, почему компиляторы С++ не могут сейчас делать что-то, требующее нескольких проходов. Да, но это все равно повредило бы производительность, но только если это действительно стало необходимым. Итак, для следующего потребуется только один проход:

void foo();
int main() { foo(); }
void foo() {}

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

int main() { foo(); }
void foo() {}

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

Мой коллега утверждает, что такая функция сэкономит много времени для разработчиков и позволит избежать проблем с совпадением декларации и определения. И я уверен, что это было предложено много раз и каждый раз отклонялось. Какова фактическая аргументация за отказ от нее, т.е. Обоснование комитета?

4b9b3361

Ответ 1

Разбор шаблонов

Рассмотрим следующую строку кода:

a < b , c > d;

Как бы вы разобрали это? На самом деле существует два способа, в зависимости от того, что a, b, c и d. Во-первых, объявление переменной

a<b,c>   d;
^^^^^^   ^
 Type   Var

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

   a<b   ,   c<d ;
   ^^^       ^^^ 
boolean expressions

в случае, если a, b, c и d - все переменные какого-либо типа.

Досадный синтаксис

Или вот еще один:

a b(c); // is 'b' a function or a variable?

Это может быть объявление функции (функция с типом возврата a и тип аргумента c) или определение переменной (тип которой a и аргумент конструктора c).

Заключение

Там, к сожалению, много чего такого. Я не уверен, если было бы невозможно написать компилятор, который мог бы заниматься такими вещами, но, по крайней мере, было бы очень сложно написать его. Время компиляции - серьезная проблема на С++. Это только ухудшит ситуацию. Также: Хорошая практика - использовать только то, что вы определили или объявили уже, даже на других языках.

Ограничения комитета

Даже если было бы разумно реализовать эту функцию, она убьет обратную совместимость. Разрешение перегрузки по функциям учитывает только предварительные объявления и интерпретация вызовов функций может изменяться в зависимости от места записи вызова функции. То, как C++ объединяется. Теперь комитет по стандартам С++ является большим по совместимости с предыдущими версиями. В частности: они не хотят нарушать существующий код (и это правильно). Ваше предложение наверняка сломает существующий код, который не подходит для разработчиков языка.

Ответ 2

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

Рассмотрим поиск двухфазных имен для шаблонов, и в частности необходимость typename. В шаблонах имена, зависящие от типа, возможно, еще не объявлены. Чтобы иметь возможность разбирать их, нам абсолютно нужно typename. Без этого синтаксический анализ остановится, и мы не можем надежно действовать, поэтому мы даже не можем указать тип, необходимый для исправления проблемы синтаксического анализа. Это проблема с курицей и яйцом: если нам нужно проанализировать строку 10 для разбора строки 5, строка 10 никогда не будет проанализирована, потому что мы сломаемся в строке 5. typename помогает нам пройти строку 5, чтобы мы могли узнать фактический тип на строка 10.

Здесь у нас будет аналогичная проблема. Рассмотрите этот код в своих предположениях:

struct Foo { };
int bar () { return Foo(); }
int Foo () { return 42; }

Чтобы разобрать этот код, нам нужно знать, обозначает ли Foo тип или функцию.