Почему атрибуты const в аргументах функции используются для перегрузки разрешения?

Возможный дубликат:
Функции с аргументами const и перегрузкой

Меня довольно путают правила перегрузки и объявления const. Вот две вещи, которые меня озадачивают, может быть, вы можете помочь мне найти более глубокое недоразумение в моей голове, что приводит к тому, что они озадачивают меня.;)

Первый выпуск:

Мой компилятор позволяет это:

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

Но следующее приводит к ошибке компиляции (функция уже имеет тело):

void f(int x) {
  std::cout << "plain f" << std::endl;
}
void f(const int x) {
  std::cout << "const f" << std::endl;
}

Я думаю, что это имеет смысл, потому что я думал, что const был только там, чтобы сообщить компилятору, что передаваемый объект не изменяется, а во втором случае он все равно копируется. Но если это правильно, то почему я могу перегружать функции, используя const?

Другими словами, почему, если я использую компиляционную версию и вызываю такие функции, как это:

  int x1 = 5;
  const int x2 = 5;
  f(x1);
  f(x2);

Я получаю "plain f" и "const f" вместо "const f" дважды? По-видимому, теперь я также использую const, чтобы сообщить компилятору, что функция вызывает не только то, что ссылка не изменяется. Это становится более запутанным, потому что, если я удалю "обычную" версию, она будет работать нормально и дважды вызовет версию "const".

Теперь, каков мой реальный вопрос? Я хотел бы знать, каковы идеи, лежащие в основе этого поведения, потому что в противном случае его запоминание очень сложно.

Ответ 1

Я думал, что const был только там, чтобы сообщить компилятору, что объект, являющийся не изменен, и во втором случае он все равно скопирован

Вы правы. Поскольку во втором случае он все равно копируется, и поэтому const не имеет никакого отношения к вызывающему, стандарт определяет, что void f(const int x) и void f(int x) имеют одну и ту же подпись. Следовательно, они сталкиваются, вы пытаетесь определить одну и ту же функцию дважды.

Так как в первом случае он не копируется в любом случае, void f(const int &x) и void f(int &x) имеют разные подписи. Поэтому они перегружаются.

В вашем первом случае запрещается вызывать int& версию f с x2 как аргумент, потому что это создаст неконстантную ссылку на объект const без какого-либо явного приведения в действие. Выполняя это, мы побеждаем цель системы const (которая заключается в том, что если вы хотите разрушить const-безопасность, вы должны сделать это явно с помощью cast). Поэтому имеет смысл иметь const- и non-const перегрузки функций со справочными параметрами.

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

Ответ 2

n3337 13.1

[Примечание: как указано в 8.3.5, объявления функций, которые имеют эквивалентные объявления параметров объявляют одну и ту же функцию и поэтому не может быть перегружен: ffer только в присутствии или отсутствии

- объявления параметров, которые di3 константы и/или летучих являются эквивалентными. То есть константа и изменчивые типы-спецификаторы для каждого типа параметров игнорируются, когда определяя, какая функция объявляется, определяется или вызывается. [ Пример:

typedef const int cInt;
int f(int);
int f(const int); // redeclaration of f(int)
int f(int) { /* ... */ } // definition of f(int)
int f(cInt) { /* ... */ } // error: redefinition of f(int)

- конец пример] Только константные и неустойчивые типы-спецификаторы в самом внешнем уровень характеристики типа параметра игнорируется в этом мода; const и volatile-спецификаторы, закопанные внутри параметра тип спецификации являются значительными и могут использоваться для различения перегруженных функций .124 В частности, для любого типа T, "указатель на T", "указатель на const T" и "указатель на volatile T" - рассмотрены различные типы параметров, как "ссылка на T", "ссылка на const T" и "ссылка на volatile T".