Как могут функционировать перегрузка и ODR сосуществовать?(C ++) - PullRequest
3 голосов
/ 06 июля 2019

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

Ответы [ 3 ]

3 голосов
/ 06 июля 2019

В C ++ определение включает типы параметров (и, начиная с C ++ 17, спецификации исключений).

Таким образом, перегрузка возможна, потому что функции не совпадают, даже если имя совпадает.

1 голос
/ 06 июля 2019

Правило единого определения означает, что каждая перегруженная функция должна быть определена один раз. Так что нет никакого противоречия. Каждая перегруженная функция в некотором роде отличается, например, количеством или типами параметров, квалификаторами c / v присутствия или отсутствия (в объявлениях параметров или самих функциях-членах класса) и т. Д.

Иногда начинающие считают, например, эти объявления функций объявлениями перегруженных функций

void f( int a[100] );
void f( int a[10] );
void f( int a[] );
void f( int *a );

Однако компилятор неявно настраивает параметр типа массива на указатель типа элемента массива.

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

Таким образом, в приведенных выше объявлениях объявляется одна и та же функция, параметры которой корректируются компилятором для типа int *.

Учтите, что эти объявления функций объявляют две перегруженные функции

void f( int *a );
void f( const int *a );

(здесь сам указатель не является константой, это данные, на которые указывает указатель, является константой)

пока эти два объявления

void f( int x );
void f( const int x );

объявляют одну и ту же функцию, потому что спецификатор const отбрасывается, когда компилятор определяет, перегружены ли функции или являются одинаковыми.

Такая же путаница может возникнуть, когда параметр функции имеет тип функции. Например

void f( void g() );
void f( void ( *g )() );

Опять же, компилятор неявно настраивает параметр типа функции для указания на функцию.

Вот демонстрационная программа

#include <iostream>

void f( void g() );
void f( void ( *g )() );

void g() { std::cout << "Hello Philippa Richter\n"; }

void f( void g() )
{
    g();
}

int main()
{
    f( g );
}

Его вывод

Hello Philippa Richter

Обратите внимание, что функция f определяется один раз, хотя она объявляется три раза, включая объявление, которое одновременно является ее определением.

0 голосов
/ 06 июля 2019

Правило единого определения не применяется к вещам с одинаковыми именами;это относится к вещам, претендующим на то, чтобы быть одинаковыми.

Два класса с именем foo (объявленные в глобальной области видимости, которые находятся вне пространства имен, или класса, или функции), претендуют на то же самое;они должны быть одинаковыми.Вы лжете компилятору, если вы меняете определения в одной программе.(Это все равно, что спорить с кем-то, основываясь на специфических определениях терминов, а затем делать вид, что доказал что-то применимое к обычным терминам.)

Две функции с именем bar не претендуют на то, что они одинаковы, если у них нетте же списки параметров.

...