Разница между аргументом переменной длины и перегрузкой функции

Этот вопрос на С++ кажется довольно простым и общим, но я все же хочу, чтобы кто-то ответил.

1) В чем разница между функцией с аргументом переменной длины и перегруженной функцией? 2) Будут ли у нас проблемы, если у нас есть функция с аргументом переменной длины и другая функция с тем же именем и с аналогичными аргументами?


person raj_arni    schedule 14.10.2009    source источник
comment
Спасибо, парни. Все ответы помогли мне разобраться в этом вопросе.   -  person raj_arni    schedule 14.10.2009


Ответы (4)


2) Вы имеете в виду следующее?

int mul(int a, int b);
int mul(int n, ...);

Предположим, что первое умножает 2 целых числа. Второй умножает n целых чисел, переданных var-args. Вызов с f(1, 2) не будет двусмысленным, потому что аргумент, переданный через «многоточие», связан с максимально возможной стоимостью. Однако передача аргумента параметру того же типа связана с наименьшими возможными затратами. Так что этот самый вызов наверняка резолвится на первую функцию :)


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

int mul(int a, int b);
int mul(double a, ...);

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

mul(3.14, 0.0); 

Это потому, что вторая функция выигрывает для первого аргумента, но первая функция выигрывает для второго аргумента. Не имеет значения, что стоимость конверсии для второго аргумента выше для второй функции, чем стоимость первого аргумента для первой функции. Как только такая ситуация «перекрестного» победителя определена, призыв к таким двум кандидатам становится двусмысленным.

person Johannes Schaub - litb    schedule 14.10.2009
comment
да, ваш убийца уходит, если кто-то вызывает f( 1, 2.0f ); и это действительно ... это проходит через список переменных параметров или оно приводит число с плавающей запятой к int и проходит через другую функцию? - person Goz; 14.10.2009
comment
прохождение через многоточие является максимально возможной стоимостью. Это всегда будет последним выбором. Преобразование float в int является стандартным преобразованием. Где-то между int -> int и int -> ellipsis :) - person Johannes Schaub - litb; 14.10.2009
comment
на самом деле mul(int,int) будет предпочтительнее, многоточие имеет самый низкий приоритет для разрешения перегрузки, поэтому приведение предпочтительнее, если существует какое-либо допустимое приведение. - person Matthieu M.; 14.10.2009
comment
ура, литб ... но все идет к тому, чтобы проиллюстрировать мою мысль о том, что могут появиться ошибки, которых вы, возможно, не ожидаете;) - person Goz; 14.10.2009
comment
@Goz, я согласен. ситуации могут быть странными. Так что, возможно, не лучшая идея слишком сильно перегружаться совершенно разными списками параметров и выходить из-под контроля :) - person Johannes Schaub - litb; 14.10.2009

1) Ну ​​перегруженная функция потребует АД много разных прототипов и реализаций. Это также будет безопасно для типов.
2) Да, это вызовет у вас проблемы, поскольку компилятор не будет знать, какую функцию ему нужно вызвать. Он может об этом предупреждать, а может и не предупреждать. Если это не так, вы можете столкнуться с трудно найти ошибки.

person Goz    schedule 14.10.2009
comment
По поводу 1) - шаблоны есть у кого? - person DevSolar; 14.10.2009
comment
Шаблоны позволяют только параметризовать тип параметра. Они не позволяют параметризовать количество параметров функции (это ограничение снято в c++0x, где есть вариативные шаблоны). - person Johannes Schaub - litb; 14.10.2009
comment
шаблоны удобны, когда это любой тип, который можно передать, но когда это переменное количество параметров того же типа, он становится менее полезным: D - person Goz; 14.10.2009
comment
Ах, вот что вы имели в виду... см. мой ответ о том, как объединить параметры, перегрузив оператор‹‹() / оператор››(). Я признаю, что это не тот синтаксис, что (...) , но это общий стиль C++ и требует только одной перегрузки для каждого принятого типа ввода. - person DevSolar; 14.10.2009
comment
Лично я ненавижу синтаксис ‹‹ и ››. Я отказываюсь его использовать. Я действительно ненавижу идею, что оператор может быть использован таким образом. Это не вяжется с логикой оператора сдвига, ИМХО... Хотя каждому свое... - person Goz; 14.10.2009
comment
Что ж, для большинства программистов на C++ ‹‹ — это первый оператор вывода. Я на самом деле слышал, как коллега сказал, что вы тоже можете использовать это? когда я показал ему алгоритм, использующий сдвиг битов. ;-) Мне нравится гибкость, которую он дает. - person DevSolar; 16.10.2009

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

Аргумент переменной длины требует наличия хотя бы одного параметра. Вам также нужен какой-то механизм для «предсказания» типа следующего параметра (так как вы должны указать его в va_arg()), и он должен быть базового типа (т. е. целое число, число с плавающей запятой или указатель). Распространенными приемами здесь являются «строки форматирования» (как в printf(), scanf()) или «списки тегов» (каждый нечетный элемент в списке параметров представляет собой перечисление, указывающее тип следующего за ним четного элемента, с нулевым перечислением, обозначающим конец список параметров).

Вообще говоря, перегрузка — это путь C++. Если вам действительно нужно что-то вроде списков аргументов переменной длины в C++, например, для удобного связывания аргументов разного числа и типа, подумайте, как работают потоки C++ (сцепленные "‹‹" и " >>"с):

class MyClass {
    public:
        MyClass & operator<<( int i )
        {
            // do something with integer
            return *this;
        }

        MyClass & operator<<( double d )
        {
            // do something with float
            return *this;
        }
};

int main()
{
    MyClass foo;
    foo << 42 << 3.14 << 0.1234 << 23;
    return 0;
}
person DevSolar    schedule 14.10.2009

Он довольно общий, и Гоз уже рассмотрел некоторые моменты. Еще несколько:

1) Переменный список аргументов дает неопределенное поведение, если вы передаете что-либо, кроме объектов POD. Перегруженные функции могут принимать любые объекты.

2) У вас может быть неоднозначность, если один член набора перегрузки принимает список переменных аргументов. Опять же, у вас может быть двусмысленность и без этого. Однако список переменных аргументов может создать двусмысленность в большем количестве ситуаций.

Первый пункт является действительно серьезным - для большинства практических целей он делает списки переменных аргументов чисто "устаревшим" элементом в C++, а не тем, что можно даже рассматривать для использования в каком-либо новом коде. Наиболее распространенной альтернативой является объединение в цепочку перегруженных операторов (например, вставки/извлечения iostream вместо printf/scanf).

person Jerry Coffin    schedule 14.10.2009