在其命名空间外部定义的类成员函数

Class member function defined outside its namespace

提问人:Igor G 提问时间:7/4/2019 最后编辑:StoryTeller - Unslander MonicaIgor G 更新时间:7/4/2019 访问量:1003

问:

以下代码与 godbolt 在线编译器资源管理器站点上提供的最新 MSVC、GCC 和 CLang 完美编译。我想知道为什么:

namespace ns
{
    struct Test
    {
        void foo();
    };
}

using namespace ns;

// Alert! Member function defined outside its namespace!
void Test::foo()
{
}

int main()
{
    ns::Test   obj;
    obj.foo();
    return 0;
}

CPPlusion 声明,如果成员函数在其类外部定义,则必须在该类的命名空间中定义该函数。请参阅有关成员函数的 cppreference 页面的最顶部。

但是,编译器仍然接受代码。这三个独立的编译器都不太可能有相同的错误,对吧?那么,他们接受这样的代码背后有充分的理由吗?

C++ C++11 命名空间 using-directives name-lookup

评论

0赞 Tarick Welling 7/4/2019
但是当你删除时会发生什么using namespace ns;
0赞 Igor G 7/4/2019
@TarickWelling 然后他们被代码噎住了,正如我所期望的那样。
4赞 BoBTFish 7/4/2019
是什么让你认为上面的代码应该是非法的?
0赞 Tarick Welling 7/4/2019
using namespace ns;表示可以预置到以下作用域中的任何函数/变量之前ns::
1赞 Tanveer Badar 7/4/2019
为什么你认为这段代码是错误的?

答:

2赞 Tarick Welling 7/4/2019 #1

using namespace ns;

5) using-directive:从在 using 指令之后对任何名称进行非限定名称查找的角度来看,直到它出现的范围结束,ns_name 中的每个名称都是可见的,就好像它是在包含 using 指令和 ns_name 的最近封闭命名空间中声明的一样。

这意味着在当前范围内,可以从该命名空间内的寻址中省略某些内容。ns

因此,当您编写以下代码时:

using namespace std;
vector<string> vectorofstrings;

你不必写

std::vector<std::string> vectorofstrings;

of a class 是类的名称。因此,如果您有:namespace

namespace aNamespace{

class aClass{
    int aMember;
    void aFunction();
};

}

然后,完全限定查找是,并且必须将函数定义为::aNamespace::aClassvoid ::aNamespace::aClass::aFunction(){}

评论

0赞 Igor G 7/4/2019
右。这些是名称查找规则。我认为名称查找仅在我使用命名实体时适用,而不是在我定义该实体时适用。
2赞 Angew is no longer proud of SO 7/4/2019
@IgorG 但是你不是在定义实体,而是在用它的名字来定义实体。::ns::Test::ns::Test::foo
4赞 Angew is no longer proud of SO 7/4/2019 #2

引用 C++ 17 (n4659) 12.2.1 [class.mfct]/1:

出现在类定义之外的成员函数定义 应出现在包含类定义的命名空间作用域中。

这意味着它必须在包含类的命名空间或该命名空间的任何父命名空间中定义。在你的例子中,它是在全局命名空间中定义的,它确实(间接)包含了类定义。

评论

0赞 Lightness Races in Orbit 7/4/2019
我承认大约一年前才意识到这一点,我仍然认为它在标准中可能有点模棱两可,尽管我并不是说 OP 的代码是无效的。我可能希望在标准文本中有一个[非规范性]的例子来敲打它。
0赞 Igor G 7/4/2019
@LightnessRacesinOrbit是的,从“出现在命名空间范围内”到“出现在任何命名空间范围内”的简单更改就足够了。太糟糕了,模糊的措辞让我看到了不存在的东西。
1赞 Lightness Races in Orbit 7/4/2019
@Angew我可以支持它。哎呀,想想看,我之所以开始这样做,是因为我从我加入的现有代码库中学到了这种风格。也许是时候让人们远离它了......
1赞 Igor G 7/4/2019
@LightnessRacesinOrbit,我绝对不会习惯这种风格。至少可以说,我认为这是非常令人困惑的。是的,一旦你达到 3 或 4 个级别,缩进命名空间是不合理的。因此,命名空间最好保持不缩进。
1赞 Lightness Races in Orbit 7/4/2019
看来我们已经达成了共识!
4赞 BoBTFish 7/4/2019 #3

12.2.1 成员函数 [class.mfct]

成员函数可以在其类定义中定义 (11.4),在这种情况下,它是一个内联成员函数 (10.1.6),或者如果已声明但未在其类定义中定义,则可以在其类定义之外定义。出现在类定义之外的成员函数定义应出现在包含类定义的命名空间作用域中。

这并不意味着定义必须出现在紧邻的范围内。它可以出现在任何封闭的命名空间中,即使它向上有几层。

但是,这将是非法的:

namespace a {
    struct X {
        void Y();
    };
}
namespace b { // not an enclosing namespace
    void X::Y()
    {
        std::cout << "Do a thing!\n";
    }
}

评论

0赞 Tarick Welling 7/4/2019
只是为了我的好奇心,如果里面也声明会发生什么.然后的定义会继续是 的定义吗?struct Xnamespace bX::Yb::X::Y
0赞 Lightness Races in Orbit 7/4/2019
@TarickWelling 当然可以。此示例中的定义与命名空间 a 中的任何内容之间没有联系。这就是命名空间的意义所在!所以你的“也在里面声明”是一种误解;它不是“也”声明的;你正在声明一个不同的、不相关的事物。struct Xnamespace b
0赞 Tarick Welling 7/4/2019
哎呀,误读了“但是,这将是非法的:”。现在我得到这个例子