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

Перегрузка функций на основе ссылки на значение по отношению к константе

Объявляет что-то вроде следующего

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.
4b9b3361

Ответ 1

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

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

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

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

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

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

Ответ 2

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

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

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

int x = 123;
foo<int>(x);  // by value
foo<int const&>(x);  // by refernce

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

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

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

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

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

void foo (int &x);

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

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