Перегрузка функций в зависимости от значения и константы - PullRequest
27 голосов
/ 29 марта 2011

Имеет ли смысл когда-либо объявлять что-то вроде следующего

void foo(int x)        { std::cout << "foo(int)"         << std::endl; }
void foo(const int &x) { std::cout << "foo(const int &)" << std::endl; }

?Как вызывающий абонент сможет различить их?Я пробовал

foo(9);  // Compiler complains ambiguous call.

int x = 9;
foo(x);  // Also ambiguous.

const int &y = x;
foo(y);  // Also ambiguous.

Ответы [ 5 ]

10 голосов
/ 29 марта 2011

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

Однако текущий стандарт языка C ++ не предлагает способ перегрузки специально для «l / r-значимости» аргументов - любое l-значение, передаваемое в качестве аргумента функции, может быть неявно преобразовано в ссылку,поэтому неоднозначность неизбежна.

C ++ 11 представляет новый инструмент для аналогичной цели - используя ссылки на r-значения , вы можете перегрузить его следующим образом

void foo(int x)        { ... }
void foo(const int &&x) { ... }

... и foo(4) (временное значение r, переданное в качестве аргумента) заставит компилятор выбрать вторую перегрузку, в то время как int i = 2; foo(i) выберет первую.

( note : даже с новым набором инструментов невозможно различить случаи 2 и 3 в вашем образце!)

3 голосов
/ 29 марта 2011

Вы можете сделать это с помощью шаблона:

template<typename T> void foo(T x) { ... }

Затем вы можете вызвать этот шаблон по значению или по ссылке:

int x = 123;
foo<int>(x);  // by value
foo<int const&>(x);  // by refernce
3 голосов
/ 29 марта 2011

Как вызывающий абонент сможет различить их?

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

1 голос
/ 29 марта 2011

Компилятор не может. Оба определения foo могут использоваться для всех «вариантов» int.

В первом foo создается копия int. Копирование int всегда возможно.

Во втором foo передается ссылка на const int. Поскольку любое int может быть приведено к const int, также может быть передана ссылка на него.

Поскольку оба варианта действительны во всех случаях, компилятор не может выбирать.

Все становится иначе, если вы, например, используйте следующее определение:

void foo (int &x);

Теперь вызов его с помощью foo(9) займет первую альтернативу, поскольку вы не можете передать 9 как неконстантную ссылку на int.

Другой пример, если вы замените int на класс, где конструктор копирования является приватным, то вызывающая сторона не сможет сделать копию значения, и первый вариант foo не будет использован.

1 голос
/ 29 марта 2011

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

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

...