Почему в параметрах C ++ последнее значение должно быть добавлено последними?

Почему в параметрах C ++ последнее значение должно быть добавлено последними?

Чтобы упростить определение языка и сохранить читаемый код.

void foo(int x = 2, int y); 

Чтобы вызвать это и использовать значение по умолчанию, вам понадобится синтаксис:

 foo(, 3); 

Вероятно, это было слишком странно. Другой альтернативой является указание имен в списке аргументов:

 foo(y : 3); 

Новый символ должен использоваться, потому что это уже означает что-то:

 foo(y = 3); // assign 3 to y and then pass y to foo. 

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

Если вы заинтересованы в других основах дизайна C ++, прочитайте статью «Дизайн и эволюция C ++» от Stroustrup.

Если вы определяете следующую функцию:

 void foo( int a, int b = 0, int c ); 

Как вы могли бы вызвать функцию и поставить значение для a и c, но оставить b по умолчанию?

 foo( 10, ??, 5 ); 

В отличие от некоторых других языков (например, Python), аргументы функции в C / C ++ не могут быть указаны по имени, например:

 foo( a = 10, c = 5 ); 

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

Представьте, что у вас была функция с этим прототипом:

 void testFunction(bool a = false, bool b = true, bool c); 

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

 testFunction(true, false); 

Как компилятор должен определить, для каких параметров я должен был указывать значения?

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

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

  int foo( int x, int y); int foo( int y) { return foo( 0, y); } 

И там у вас есть эквивалент:

  int foo( int x = 0, int y); 

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

(Это применяется к __cdecl, который, как правило, используется для объявлений VC ++ и __stdcall).

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

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

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

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

Речь идет о конвенции о вызовах. Call Convention: когда вы вызываете функцию, параметры вставляются в стек справа налево. например

 fun(int a, int b, int c); 

стек выглядит так: a b c, поэтому, если вы установите значение по умолчанию слева направо, как это:

 fun(int a = 1, int b = 2, int c); 

и вызывайте так:

 fun(4,5); 

ваш вызов означает, что a = 4, b = 5 и c = нет значения; // что не так!

если вы объявите функцию следующим образом:

fun(int a, int b = 2, int c = 3);

и вызывайте так: fun(4, 5);

ваш метод вызова устанавливает a = 4, b = 5 и c = значение по умолчанию (3); // какой правильный!

В заключение вы должны поместить значение по умолчанию справа налево.

Цзин Цзэн прав. Я хотел бы добавить свои замечания здесь. Когда вызывается функция, аргументы вставляются в стек справа налево. Например, допустим, у вас есть эта произвольная функция.

 int add(int a, int b) { int c; c = a + b; return c; } 

Вот кадр стека для функции:

 ------ b ------ a ------ ret ------ c ------ 

Эта диаграмма выше представляет собой стек стека для этой функции! Как вы можете видеть, сначала b вставляется в стек, а затем вставляется в стек. После этого адрес возврата функции помещается в стек. Адрес возврата функции содержит местоположение в main (), из которого была вызвана функция, и после выполнения функции выполнение программы переходит к обратному адресу этой функции. Затем любые локальные переменные, такие как c, помещаются в стек.

Теперь самое главное, что аргументы переносятся в стек справа налево. В принципе, любые заданные по умолчанию параметры являются буквальными значениями, которые хранятся в разделе кода исполняемого файла. Когда выполнение программы встречает параметр по умолчанию без соответствующего аргумента, он толкает это буквальное значение в верхнюю часть стека. Затем он смотрит на a и толкает значение аргумента в верхнюю часть стека. Указатель стека всегда указывает на верхнюю часть стека, самую последнюю нажатую переменную. Таким образом, любые литеральные значения, которые вы вставляете в стек по умолчанию, являются «позади» указателя стека.

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

  • Как просмотреть исходный код для функции?
  • Разница между . и: в Луа
  • «Правильный» способ указать необязательные аргументы в R-функциях
  • Как я могу создать функцию с обработчиком завершения в Swift?
  • Определить имена таблиц и столбцов в качестве аргументов в функции plpgsql?
  • Как сделать .lib-файл, когда есть .dll-файл и заголовочный файл
  • Вычисление перекрестного произведения двух векторов в Fortran 90
  • Передача функций в качестве аргументов в Matlab
  • Передача целочисленного значения по ссылке в Python
  • Выделить память 2d массива в функции C
  • Как программно создать функцию R?
  • Давайте будем гением компьютера.