Зачем добавлять параметры по умолчанию в 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++, прочитайте статью The Design and Evolution of C++ от Stroustrup.
Почему мы не можем использовать foo (3) вместо foo (, 3)? Есть ли какие-либо проблемы, которые мешают компилятору понять намерение программиста? Я думаю, что лучшей причиной было бы уменьшить вероятность двусмысленности во время компиляции и упростить создание компиляторов. – user95319
В D & E Строуструп говорит, что именованный параметр идиомы сложный в C++ из-за разделения функций-декларации и определения функции. Если пользователь использует одно имя в объявлении, а другое - в реализации, компилятор должен будет пренебречь им. – Abhay
Абхай - я не думаю, что он говорит это - я думаю, он * сообщает *, что некоторые люди в комитете думали об этом и объясняли их причины. Насколько нам известно, сам Страуструп был бы в порядке с зависимостью от имен в декларации и игнорированием определения (я бы хотел). –
Если определить следующую функцию:
void foo(int a, int b = 0, int c);
Как бы вы вызвать функцию и обеспечить значение для а и с, но оставить б по умолчанию?
foo(10, ??, 5);
В отличие от некоторых других языков (например, Python), аргументы функции в C/C++ не могут быть квалифицированы по имени, как следующее:
foo(a = 10, c = 5);
Если бы это было возможно, то аргументы по умолчанию может быть где угодно в списке.
Что касается случаев, которые вроде ниже..foo (int a, double b = 0.0, char c = 'a') – yesraaj
на самом деле, если компилятор достаточно умный, мы должны иметь возможность писать foo (10, 5). Компилятор должен интерпретировать его как foo (10, default, 5). – user95319
@raj: Это разрешено в C++. Вы можете подумать об этом как о чем-то вроде того, как только вы начали давать аргументы по умолчанию, вы должны полностью довести до конца (т. Е. До последнего параметра), не оставляя ничего между ними. – Naveen
Представьте, что вы имели функцию с этим прототипом:
void testFunction(bool a = false, bool b = true, bool c);
Теперь предположим, что я назвал функцию так:
testFunction(true, false);
Как компилятор должен выяснить, какие параметры я хотел поставить значения для?
Это будет понятно, потому что для этого были бы правила. Например, аргументы будут привязываться ко второму и третьему параметрам. – juanchopanza
Как правило, параметры функции обрабатываются компилятором и помещаются в стек в порядке слева направо. Поэтому имеет смысл, что сначала необходимо оценить все параметры со значениями по умолчанию.
(Это относится к __cdecl, который имеет значение по умолчанию для объявлений VC++ и __stdcall).
Это вовсе не указывается стандартом языка, поэтому это не может быть причиной того, как работает язык - это два совершенно разных уровня беспокойства. –
Я признаю, что я этого не осознавал, но я знаю, что это bahaviour компиляторов VC++ и gcc. – ChrisBD
Потому что он использует относительное положение аргументов, чтобы найти, какие параметры они соответствуют.
Возможно, он использовал типы для идентификации того, что необязательный параметр не указан. Но неявное преобразование может помешать ему. Еще одна проблема заключалась бы в ошибках программирования, которые можно было бы интерпретировать как необязательные аргументы, вместо отсутствующей ошибки аргумента.
Чтобы дать возможность любому аргументу стать факультативным, должен быть способ идентифицировать аргументы, чтобы убедиться, что нет ошибки программирования или устранения неоднозначностей. Это возможно на некоторых языках, но не на C++.
Как видно из большинства ответов, параметры по умолчанию, потенциально доступные в списке параметров, увеличивают сложность и неоднозначность вызовов функций (как для компилятора, так и, что более важно, для пользователей этой функции).
Одна хорошая вещь о C++ заключается в том, что часто вы можете делать то, что хотите (даже если это не всегда хорошая идея). Если вы хотите, чтобы иметь аргументы по умолчанию для различных позиций параметров, вы можете почти наверняка сделать это, написав перегрузки, которые просто оборачиваются и называют полностью параметризированную встраиваемую функцию:
int foo(int x, int y);
int foo(int y) {
return foo(0, y);
}
И там у Вас есть эквивалент:
int foo(int x = 0, int y);
Еще одна вещь, которую комитет по стандартизации должен был рассмотреть, заключается в том, как параметры по умолчанию взаимодействуют с другими функциями, такими как перегруженные функции, разрешение шаблона и поиск имени. Эти функции взаимодействуют в очень сложных и трудных для описания способах уже. Предоставление параметров по умолчанию в любом месте может только повысить сложность.
Вопрос о вызове. Call Convention: Когда вы вызываете функцию, параметры вставляются в стек справа налево. , например.
fun(int a, int b, int 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);
ваш вызов означает установить = 4, B = 5, С = значение по умолчанию (3); // какой правильный!
В заключение вы должны поместить значение по умолчанию справа налево.
Цзин Цзэн является правильным. Я хотел бы добавить свои замечания здесь. Когда вызывается функция, аргументы вставляются в стек справа налево. Например, допустим, у вас есть эта произвольная функция.
int add(int a, int b) {
int c;
c = a + b;
return c;
}
Вот кадр стека для функции:
------
b
------
a
------
ret
------
c
------
Эта диаграмма выше кадр стека для этой функции! Как вы можете видеть, сначала b вставляется в стек, а затем вставляется в стек. После этого адрес возврата функции помещается в стек. Адрес возврата функции содержит местоположение в main(), из которого была вызвана функция, и после выполнения функции выполнение программы переходит к обратному адресу этой функции. Затем любые локальные переменные, такие как c, помещаются в стек.
Теперь самое главное, что аргументы переносятся в стек справа налево. В принципе, любые заданные по умолчанию параметры являются буквальными значениями, которые хранятся в разделе кода исполняемого файла. Когда выполнение программы встречает параметр по умолчанию без соответствующего аргумента, он толкает это буквальное значение в верхнюю часть стека. Затем он смотрит на a и толкает значение аргумента в верхнюю часть стека. Указатель стека всегда указывает на верхнюю часть стека, самую последнюю нажатую переменную. Таким образом, любые литеральные значения, которые вы вставляете в стек по умолчанию, являются «позади» указателя стека.
Скорее всего, для компилятора было бы, скорее всего, быстрее сначала перенести произвольные значения по умолчанию в стек, поскольку они не хранятся в ячейке памяти, и быстро наращивают стек. Подумайте о том, что было бы, если бы переменные были вначале вытолкнуты в стек, а затем в литералы. Доступ к ячейке памяти для процессора занимает относительно много времени по сравнению с вытягиванием буквального значения из схемы или регистров CPU. Поскольку для перетаскивания переменных в стек против литералов требуется больше времени, литералы должны будут ждать, тогда обратный адрес должен будет ждать, и локальные переменные также должны будут ждать. Вероятно, это не большая проблема эффективности, но это только моя теория, почему аргументы по умолчанию всегда находятся в крайних правых положениях заголовка функции на C++. Это означает, что компилятор был разработан как таковой.
Это не бесполезно. Понимание дизайна языка (и, в частности, дизайн используемого вами языка), следует поощрять ИМО. Для этого, помимо прочего, вам нужно выяснить, почему ваши идеи для «очевидных, простых» улучшений непрактичны, если они непрактичны. И почему дизайнеры этого языка упустили возможность, если они не являются непрактичными. Я уверен, что языковой дизайн связан с программированием. –