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

Does the order of overloaded function declaration matter in c++?

我目前定义了一个多次重载的函数。对于某些重载定义,它会使用不同类型的输入调用相同的函数。因此,函数 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

我认为这个问题可以归结为 "does the compiler only check whether function has been defined or does it check whether the function has been defined on particular input?"

重载的声明顺序无关紧要,因为以下是等价的:

// 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),要么程序是 ill-formed 因为您还没有声明Foo(B) 通话前。

Does the compiler only check whether function has been defined

一般来说,编译器根本不去检查函数是否已经定义。但是,函数必须在调用之前声明。

简短的回答是肯定的——顺序很重要。 (此外,它实际上与重载无关 - 您可以将 Foo(B) 重命名为 Goo(B)。)

针对您的特定问题的一种常见补救措施是 forward-declare 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) {
    // ...
}

编译器需要了解该特定函数 - 它必须在使用前声明。但是,它可以在别处定义。在 link 时,收集所有编译器输出并且符号 "linked" 在一起 - linker 将弄清楚如何生成正确的指令以从该行调用 Foo(B) ,或者可能将其内联,等等

有时函数必须是 forward-declared。例如

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

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

FooGoo 都需要了解彼此,因此您可以在 Foo() 的定义之前声明两者(或将它们放在 header 中,如果合适的话)。

Does the order of overloaded function declaration matter in c++?

简短回答:是的。 C++ 中的顺序很重要。举个例子:

i = 45;
int i;

这会导致错误(当然假设更高范围内没有其他i)。它是变量、函数、class 还是其他什么都没有关系;在 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());
}

注意到这个程序有什么有趣的地方了吗?我们可以在main()定义之前调用Foo(A)。这是将 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);

Re-arranging 这个块的顺序无关紧要。所以,我们也可以这样做:

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

只要这些声明出现在所有定义之前,我们仍然很好。