Имеет ли значение порядок объявления перегруженных функций в c ++? - PullRequest
3 голосов
/ 11 июля 2019

В настоящее время я определяю функцию, которая перегружается несколько раз. Для некоторых перегруженных определений вызывается одна и та же функция с разными типами ввода. Таким образом, функция Foo определена для получения типа A, но в теле функции она вызывает Foo для типа B. Однако Foo для типа B определяется после определения A.

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

void Foo (A input) {
   B b = B();
   Foo(b);
}

void Foo (B input) {
   printf("%s", input.toString());
}

int main() {
   A a = A();
   Foo(a);
   return 0;
}

//code has been oversimplified

Я думаю, что вопрос можно свести к следующему: «Компилятор только проверяет, была ли определена функция, или он проверяет, была ли определена функция для определенного ввода?»

Ответы [ 3 ]

5 голосов
/ 11 июля 2019

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

// 1.
void foo(int);
void foo(double);
foo(42);

// 2.
void foo(double);
void foo(int);
foo(42);

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

// 3.
void foo(int);
foo(42);
void foo(double);

// 4.
void foo(double);
foo(42);
void foo(int);

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


В вашем примере программы Foo(A) либо имеет бесконечную рекурсию (если B неявно преобразуется в A), или программа некорректна, так как вы не объявили Foo(B) до вызова.

Компилятор только проверяет, была ли определена функция

В общем, компилятор вообще не проверяет, была ли определена функция.Однако функция должна быть объявлена ​​до того, как она может быть вызвана.

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

Короткий ответ - да, порядок имеет значение. (Кроме того, это не имеет ничего общего с перегрузками - вы можете переименовать Foo(B) в Goo(B).)

Одним из распространенных способов решения вашей конкретной проблемы является предварительное объявление Foo(B):

// Forward declaration
void Foo(B);

void Foo (A input) {
   B b = B();
   Foo(b);     // Compiler now knows about Foo(B), so this is fine.
}

void Foo (B input) {
    // ...
}

Компилятор должен знать об этой конкретной функции - она ​​должна быть объявлена ​​ перед использованием. Однако он может быть определен в другом месте. Во время соединения собраны все выходные данные компилятора, и символы «связаны» вместе - компоновщик выяснит, как создать правильные инструкции для вызова Foo(B) из этой строки, или, возможно, встроить ее и т. Д.

В некоторых случаях функция должна быть объявлена ​​заранее. Э.Г.

void Foo() {
   if (condition) Goo();
}

void Goo() {
   if (condition) Foo();
}

Оба Foo и Goo должны знать друг о друге, так что вы можете объявить оба до определения Foo() (или поместить их в заголовок, если необходимо).

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

Имеет ли значение порядок объявления перегруженных функций в c ++?

Краткий ответ: Да. Порядок имеет значение в C ++. Возьмите следующий пример:

i = 45;
int i;

Это может привести к ошибке (при условии, что, конечно, нет другого i в более высоком объеме). Неважно, если это переменная, функция, класс или что-то еще; в C ++ символ должен быть объявлен перед использованием. Даже если это перегруженная функция, любая перегрузка, которую вы используете в определении, должна идти первой.

Аккуратный трюк

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

void Foo (A input);
void Foo (B input);

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

Как это помогает нам? Хорошо, рассмотрим следующую программу (которая работает, кстати):

void Foo (A input);
void Foo (B input);

int main() {
   A a = A();
   Foo(a);
   return 0;
}

void Foo (A input) {
   B b = B();
   Foo(b);
}
void Foo (B input) {
   printf("%s", input.toString());
}

Заметили что-нибудь интересное об этой программе? Мы можем вызвать Foo(A) in main() до того, как оно будет определено. Это хорошо, если поместить объявление Foo(A) выше определения main(). Компилятор знает, что Foo(A) существует, поэтому мы можем вызвать его с main(), даже если у нас пока нет его определения.

Самое интересное в использовании таких объявлений, как это, заключается в том, что как только у нас есть объявления , , мы можем поместить определения в любом порядке . Таким образом, мы можем сделать, например, это:

void Foo (A input);
void Foo (B input);

int main() {
   A a = A();
   Foo(a);
   return 0;
}

void Foo (B input) {
   printf("%s", input.toString());
}

void Foo (A input) {
   B b = B();
   Foo(b);
}

или это:

void Foo (A input);
void Foo (B input);

void Foo (A input) {
   B b = B();
   Foo(b);
}

int main() {
   A a = A();
   Foo(a);
   return 0;
}


void Foo (B input) {
   printf("%s", input.toString());
}

Или даже это:

void Foo (B input) {
   printf("%s", input.toString());
}

int main() {
   A a = A();
   Foo(a);
   return 0;
}

void Foo (A input) {
   B b = B();
   Foo(b);
}

Поскольку все это приходит после деклараций , , порядок определения в данном случае не имеет значения .


Стоит упомянуть, прежде чем я уйду: если у нас есть блок объявлений вроде этого:

void Foo (A input);
void Foo (B input);

Изменение порядка в этом блоке не имеет значения. Итак, мы могли бы сделать это также:

void Foo (B input);
void Foo (A input);

Пока эти заявления предшествуют всем определениям, мы все еще хороши.

...