为什么友元函数不被视为 class 的命名空间的成员,它是在没有额外声明的情况下声明的?
Why is a friend function not treated as a member of a namespace of a class it was declared in without additional declaration?
假设我们有一个来自命名空间 space
的 class foo
,它声明了一个名为 bar
的 friend
函数,稍后将定义它,例如所以:
namespace space {
struct foo {
friend void bar(foo);
};
}
namespace space {
void bar(foo f) { std::cout << "friend from a namespace\n"; }
}
据我了解,friend void bar(foo);
声明bar
是一个自由函数在space
按值取 foo
。要使用它,我们可以简单地做:
auto f = space::foo();
bar(f);
我的理解是不用说space::bar
,因为ADL会看到bar
和[=一样在同一个namespace
中定义19=](它的参数)并允许我们省略全名限定。尽管如此,我们被允许对其进行限定:
auto f = space::foo();
space::bar(f);
哪个有效(并且应该有效)完全相同。
当我引入其他文件时,事情开始变得奇怪。假设我们将 class 和声明移动到 foo.hpp
:
#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP
namespace space {
struct foo {
friend void bar(foo);
};
}
#endif //PROJECT_FOO_HPP
和foo.cpp
的定义:
#include "foo.hpp"
#include <iostream>
namespace space {
void bar(foo f) { std::cout << "friend from a namespace\n"; }
}
请注意,我所做的只是 将 (没有更改任何代码)的东西移动到 .hpp
-.cpp
对。
然后发生了什么?好吧,假设我们#include "foo.hpp"
,我们仍然可以做到:
auto f = space::foo();
bar(f);
但是,我们已经做不到了:
auto f = space::foo();
space::bar(f);
这没有说明:error: 'bar' is not a member of 'space'
,这很令人困惑。我相当确定 bar
是 space
的成员,除非我严重误解了某些内容。同样有趣的是,如果我们另外声明(再次!)bar
,但 在 foo
之外 ,它会起作用。我的意思是,如果我们将 foo.hpp
更改为:
#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP
namespace space {
struct foo {
friend void bar(foo);
};
void bar(foo); // the only change!
}
#endif //PROJECT_FOO_HPP
现在可以使用了。
是否有一些头文件/实现文件改变了预期的(至少对我而言)行为?这是为什么?这是一个错误吗?我正在使用 gcc 版本 10.2.0(Rev9,由 MSYS2 项目构建)。
friend
声明有一点微妙之处,虽然它不需要先前的函数声明或 class 你的 class 是友好的,但不会使函数除了通过 ADL 之外的查找可见。
A name first declared in a friend declaration within a class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided - see namespaces for details.
这就是为什么您能够找到 bar(f)
(执行 ADL)但找不到 space::bar(f)
(完全限定名称意味着未调用 ADL)的原因。
通过 ADL 找不到 bar
的调用代码需要查看声明。在所有内容都在一个文件中的版本中,调用代码将看到 space::foo
的完整定义。当您将其拆分为一个 HPP 和一个 CPP 文件时,调用代码只能看到提供有限可访问性的友元声明。
如您所见,如果要通过普通查找使函数可见,请在“foo.hpp”中放置 foo
的声明:
#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP
namespace space {
struct foo {
friend void bar(foo);
};
void bar(foo); // Now code that includes foo.hpp will see a declaration for bar
}
#endif //PROJECT_FOO_HPP
假设我们有一个来自命名空间 space
的 class foo
,它声明了一个名为 bar
的 friend
函数,稍后将定义它,例如所以:
namespace space {
struct foo {
friend void bar(foo);
};
}
namespace space {
void bar(foo f) { std::cout << "friend from a namespace\n"; }
}
据我了解,friend void bar(foo);
声明bar
是一个自由函数在space
按值取 foo
。要使用它,我们可以简单地做:
auto f = space::foo();
bar(f);
我的理解是不用说space::bar
,因为ADL会看到bar
和[=一样在同一个namespace
中定义19=](它的参数)并允许我们省略全名限定。尽管如此,我们被允许对其进行限定:
auto f = space::foo();
space::bar(f);
哪个有效(并且应该有效)完全相同。
当我引入其他文件时,事情开始变得奇怪。假设我们将 class 和声明移动到 foo.hpp
:
#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP
namespace space {
struct foo {
friend void bar(foo);
};
}
#endif //PROJECT_FOO_HPP
和foo.cpp
的定义:
#include "foo.hpp"
#include <iostream>
namespace space {
void bar(foo f) { std::cout << "friend from a namespace\n"; }
}
请注意,我所做的只是 将 (没有更改任何代码)的东西移动到 .hpp
-.cpp
对。
然后发生了什么?好吧,假设我们#include "foo.hpp"
,我们仍然可以做到:
auto f = space::foo();
bar(f);
但是,我们已经做不到了:
auto f = space::foo();
space::bar(f);
这没有说明:error: 'bar' is not a member of 'space'
,这很令人困惑。我相当确定 bar
是 space
的成员,除非我严重误解了某些内容。同样有趣的是,如果我们另外声明(再次!)bar
,但 在 foo
之外 ,它会起作用。我的意思是,如果我们将 foo.hpp
更改为:
#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP
namespace space {
struct foo {
friend void bar(foo);
};
void bar(foo); // the only change!
}
#endif //PROJECT_FOO_HPP
现在可以使用了。
是否有一些头文件/实现文件改变了预期的(至少对我而言)行为?这是为什么?这是一个错误吗?我正在使用 gcc 版本 10.2.0(Rev9,由 MSYS2 项目构建)。
friend
声明有一点微妙之处,虽然它不需要先前的函数声明或 class 你的 class 是友好的,但不会使函数除了通过 ADL 之外的查找可见。
A name first declared in a friend declaration within a class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided - see namespaces for details.
这就是为什么您能够找到 bar(f)
(执行 ADL)但找不到 space::bar(f)
(完全限定名称意味着未调用 ADL)的原因。
通过 ADL 找不到 bar
的调用代码需要查看声明。在所有内容都在一个文件中的版本中,调用代码将看到 space::foo
的完整定义。当您将其拆分为一个 HPP 和一个 CPP 文件时,调用代码只能看到提供有限可访问性的友元声明。
如您所见,如果要通过普通查找使函数可见,请在“foo.hpp”中放置 foo
的声明:
#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP
namespace space {
struct foo {
friend void bar(foo);
};
void bar(foo); // Now code that includes foo.hpp will see a declaration for bar
}
#endif //PROJECT_FOO_HPP