namespace::function() 声明什么时候有用?

When is a namespace::function() declaration useful?

单个文件示例

这是一个使用命名空间的简单程序。

#include <iostream>

namespace foo {
    void hello();
}

void foo::hello() {
    std::cout << "hello\n";
}

void foo::hello(); // Why is this syntax allowed? When is it useful?

int main() {
    foo::hello();
}

此程序编译良好并产生预期的输出。

$ ./a.out
hello

我想知道 void foo::hello(); 声明什么时候有用?在这个程序中,显然这个声明是多余的。但是既然存在这个语法,这在其他一些场景中一定有用吗?

双文件示例

这里有一个例子,说明 void foo::hello(); 声明单独存在是没有用的。

// foo.cpp
#include <iostream>

namespace foo {
    void hello();
}

void foo::hello() {
    std::cout << "foo::hello\n";
}
// main.cpp
void foo::hello(); // This does not work!

int main()
{
    foo::hello();
}

尝试编译以上两个文件会导致以下错误:

$ clang++ foo.cpp main.cpp
main.cpp:1:6: error: use of undeclared identifier 'foo'
void foo::hello();
     ^
main.cpp:5:5: error: use of undeclared identifier 'foo'
    foo::hello();
    ^
2 errors generated.

在上面的示例中修复 main.cpp 的唯一方法似乎是包含 namespace 声明:

// main.cpp - fixed
namespace foo {
    void hello();
}

void foo::hello(); // But now this is redundant again!

int main()
{
    foo::hello();
}

所以在我们让这段代码正确编译之后,void foo::hello(); 声明又显得多余了。这样的声明是否会在任何情况下发挥有用的作用?

来自[basic.def]/1

A declaration may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. If so, the declaration specifies the interpretation and attributes of these names.

允许这样的代码

#include <iostream>

void foo();
void foo();
void foo();
void foo();
void foo();

int main()
{
    foo();
}

void foo() { std::cout << "foo()"; }

有效。一个函数有多个声明没有坏处,只要我们只有一个定义,就不会造成问题。


另一个例子是你有一个全局函数,你想成为多个 classes 的朋友。您将在每个 class header 中包含该函数原型,以便您可以将其添加为好友,然后您将在主源文件中包含所有这些 class header。所以

A.h
void foo();
struct A { friend void foo(); }

B.h
void foo();
struct B { friend void foo(); }

main.cpp
#include "A.h"
#include "B.h"

...

然后将转换为

main.cpp
void foo();
struct A { friend void foo(); }

void foo();
struct B { friend void foo(); }

...

所以我们希望这是合法的。

在大多数情况下,在 C++ 中,对于任何可以在不定义它的情况下声明或可以完全定义的事物,该事物的声明或定义可以出现在所有相同的上下文中。所以这可能只是保持模式一致的一种方式。

C++ 标准不会特意禁止那些作为其其他规则的逻辑结果但无用的事情,只要相当清楚如果有人这样做会发生什么。如果它确实添加了这些限制,那将给编译器编写者带来额外的工作,而没有真正的好处。