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

3
以下代码在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;
}

cppreference声称,如果成员函数在类外定义,则必须在该类的命名空间中定义。请参见有关成员函数的cppreference页面的顶部。
但是,编译器仍然接受该代码。三个独立的编译器很不可能都有相同的错误,对吧?那么,它们接受这样的代码背后是否有充分的理由呢?

但是当您删除 using namespace ns; 时会发生什么? - Tarick Welling
4
你认为上面的代码为什么应该是非法的? - BoBTFish
using namespace ns; 的意思是在下面的作用域中,可以在任何函数/变量之前加上 ns:: - Tarick Welling
1
你认为这个代码为什么是错误的? - Tanveer Badar
@BoBTFish cppreference声称,如果一个成员函数在类外定义,那么它必须在该类的命名空间中定义。请参见页面顶部https://en.cppreference.com/w/cpp/language/member_functions - Igor G
显示剩余3条评论
3个回答

4

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

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

这意味着它必须在包含该类的命名空间或该命名空间的任何父级命名空间中进行定义。 在您的情况下,它是在全局命名空间中定义的,该全局命名空间确实(间接地)包含了类定义。


@IgorG 我认为我正在寻找一些明确的符号,表明using namespace声明的结果适用于函数定义前导中提到的名称,因为尽管没有任何禁止它的东西,但在某种程度上它似乎是不太明显的。 - Lightness Races in Orbit
@LightnessRacesinOrbit,我强烈反对这种方法(在我们的代码库中积极打击它),原因就是自由函数。通过遵循不为命名空间缩进的代码风格,可以更容易地避免额外的缩进。 - Angew is no longer proud of SO
1
@Angew 我可以支持这个。想想看,我之所以只用这种方式是因为我从一个已有的代码库中学到了这种风格,而我当时加入了这个代码库。也许现在是时候让人们远离它了... - Lightness Races in Orbit
1
@LightnessRacesinOrbit,我绝对不会习惯这种风格。我认为它极其混乱,至少可以这么说。而且,一旦你达到了3或4个级别,缩进命名空间是不合理的。因此,最好保持命名空间不缩进。 - Igor G
1
看起来我们达成了共识! - Lightness Races in Orbit
显示剩余3条评论

4

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";
    }
}

只是出于好奇,如果在“命名空间b”中也声明了“struct X”,那么“X :: Y”的定义是否会成为“b :: X :: Y”的定义? - Tarick Welling
@TarickWelling 当然可以。这个例子中的定义与命名空间a中的任何内容都没有关系,这就是命名空间的用途所在!因此,你的“struct X也在namespace b中声明”是一种误解;它不是“也”被声明;你正在声明一个不同的、无关的事物。 - Lightness Races in Orbit
糟糕,我误读了“但是,这是非法的:”。现在我明白了这个例子。 - Tarick Welling

2

5) 使用指令:从使用指令后到它所在的作用域结束,任何名称的未限定名称查找都会将ns_name中的每个名称视为在最近的封闭命名空间中声明了该名称。

这意味着在当前作用域中,可以省略ns来访问该命名空间中的某些内容。

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

using namespace ns;

您可以直接使用该命名空间中的名称。

using namespace std;
vector<string> vectorofstrings;

最初的回答

您不必编写

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

最初的回答:
一个类的命名空间就是这个类的名称。所以如果你有以下代码:
需要翻译的内容:

namespace of a class is the name of the class. So if you have:

namespace aNamespace{

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

}

那么完全限定查找是::aNamespace::aClass,函数必须定义为void ::aNamespace::aClass::aFunction(){}的一部分。


没错,那些是名称查找规则。我认为名称查找仅适用于当我“使用”命名实体时,而不是当我“定义”该实体时。 - Igor G
2
@IgorG 但是你没有定义实体::ns::Test,你正在使用它的名称来定义实体::ns::Test::foo - Angew is no longer proud of SO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接