重载函数声明的顺序在c ++中重要吗?

时间:2019-07-10 22:47:24

标签: c++ function overloading declaration

我目前定义了一个函数,该函数会多次重载。对于某些重载的定义,它使用不同的输入类型调用相同的函数。因此,将函数Foo定义为采用类型A,但在函数体内,它将对类型B调用Foo。但是,对类型B的Foo的定义是在定义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 个答案:

答案 0 :(得分:5)

在以下等价的意义上,重载的声明顺序无关紧要:

// 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),或者由于您未声明{{1} }。

  

编译器是否仅检查功能是否已定义

通常,编译器根本不检查是否已定义函数。但是,必须先声明函数才能调用。

答案 1 :(得分:3)

答案很简单-顺序很重要。 (此外,它确实与重载无关-您可以将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();
}

FooGoo都需要彼此了解,因此您可以在Foo()的定义之前声明两者(或在适当的情况下将它们放在标题中)。

答案 2 :(得分:1)

  

重载函数声明的顺序在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)中调用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);

只要这些声明先于所有定义,我们还是不错的。