3

The following code compiles perfectly with the latest MSVC, GCC and CLang available at godbolt online compiler explorer site. I wonder why:

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 claims that if a member function is defined outside its class, then it must be defined at the namespace of that class. See the very top of the cppreference page about member functions.

But still, the compilers accept the code. It's really unlikely that all three independent compilers have the same bug, right? So, is there a good reason behind them accepting such code?

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
Igor G
  • 1,838
  • 6
  • 18

3 Answers3

4

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

A member function definition that appears outside of the class definition shall appear in a namespace scope enclosing the class definition.

This means it must be defined in the namespace which contains the class, or any parent namespace of that namespace. In your case, it's defined in the global namespace, which does indeed enclose (indirectly) the class definition.

Tarick Welling
  • 3,119
  • 3
  • 19
  • 44
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • I admit to only becoming aware of this a year or so ago, and I still think that it's perhaps a _little_ ambiguous in the standard, though I am not suggesting that the OP's code is invalid. I'd perhaps like a [non-normative] example in the standard text to hammer it home. – Lightness Races in Orbit Jul 04 '19 at 13:02
  • @LightnessRacesinOrbit yes, a simple change from "appear in a namespace scope" to "appear in _any_ namespace scope" would suffice. Too bad that vague wording lead me to see things that weren't there. – Igor G Jul 04 '19 at 13:15
  • @IgorG I think I'm looking for some explicit notation that the result of a `using namespace` declaration is applicable to the names mentioned in the preamble of a function definition, because although there's nothing prohibiting it, it _does_ seem kind of non-obvious to an extent – Lightness Races in Orbit Jul 04 '19 at 13:44
  • @IgorG Also don't get _too_ used to this; since I started taking this approach (avoids an extra indent!) I routinely forget that it won't work on free functions, so I keep defining them in the wrong namespace and get unresolved externals from the original namespaced declarations – Lightness Races in Orbit Jul 04 '19 at 13:45
  • @LightnessRacesinOrbit I heavily discourage this approach (actively fight it in our codebase), precisely because of free functions. It's easier to avoid the extra indent by following a code style which doesn't indent for namespaces. – Angew is no longer proud of SO Jul 04 '19 at 13:51
  • 1
    @Angew I can get behind that. Heck, come to think of it, I only ever started doing it _this_ way because I learnt it from an extant codebase I was joining which used that style. Perhaps it's time to push people away from it... – Lightness Races in Orbit Jul 04 '19 at 13:53
  • 1
    @LightnessRacesinOrbit, I'm absolutely NOT going to get used to this style. I see it as extremely confusing, to say the least. And yes, indenting namespaces is unreasonable once you reach, say, 3 or 4 levels. So, namespaces'd better stay unindented. – Igor G Jul 04 '19 at 13:55
  • 1
    Looks like we have a consensus! – Lightness Races in Orbit Jul 04 '19 at 13:59
4

12.2.1 Member functions [class.mfct]

A member function may be defined (11.4) in its class definition, in which case it is an inline member function (10.1.6), or it may be defined outside of its class definition if it has already been declared but not defined in its class definition. A member function definition that appears outside of the class definition shall appear in a namespace scope enclosing the class definition.

This does not mean the definition must appear in the immediately surrounding scope. It can appear in any enclosing namespace, even if that is several layers up.

However, this would be illegal:

namespace a {
    struct X {
        void Y();
    };
}
namespace b { // not an enclosing namespace
    void X::Y()
    {
        std::cout << "Do a thing!\n";
    }
}
BoBTFish
  • 19,167
  • 3
  • 49
  • 76
  • Just for my curiosity, what would happen if `struct X` was also declared inside `namespace b`. Would the definition of `X::Y` then go on to be the definition of `b::X::Y`? – Tarick Welling Jul 04 '19 at 13:03
  • @TarickWelling Yes of course. There is no connection between the definition in this example and anything in namespace a. That's the point of namespaces! So your "`struct X` was also declared inside `namespace b`" is a misapprehension; it's not "also" declared; you're declaring a different, unrelated thing. – Lightness Races in Orbit Jul 04 '19 at 13:45
  • Whoops misread the "However, this would be illegal:". Now I get the example – Tarick Welling Jul 04 '19 at 14:43
2

using namespace ns;

5) using-directive: From the point of view of unqualified name lookup of any name after a using-directive and until the end of the scope in which it appears, every name from ns_name is visible as if it were declared in the nearest enclosing namespace which contains both the using-directive and ns_name.

It means that in the current scope ns can be omitted from addressing something inside that namespace.

As such when you write this code:

using namespace std;
vector<string> vectorofstrings;

You don't have to write

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

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

namespace aNamespace{

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

}

Then the fully qualified lookup is ::aNamespace::aClass and a function must be defined as being part of void ::aNamespace::aClass::aFunction(){}

Tarick Welling
  • 3,119
  • 3
  • 19
  • 44