1

I am working through Bjarne Stroustrup's "The C Programming Language (4th edition)" and I came across an example in section 14.4.6 (Namespaces - Versioning) that I haven't been able to replicate. The example #includes a namespaced class declaration into another namespace. Here is my simplified attempt at reproducing this example:

// V3.h

namespace V3 {
    class C {
        public:
            void print();
    };
}

// V3.cpp

#include <iostream>
#include "V3.h"

using namespace std;

namespace V3 {
    void C::print() {
        cout << "Hello from C" << endl;
    }
}

// Popular.h

namespace Popular {
    #include "V3.h"
}

// main.cpp

#include "Popular.h"

int main() {
    Popular::V3::C c;
    c.print();
}

When I attempt to compile this program I get the following output:

$ g++ main.cpp V3.cpp
/tmp/ccAVnUZi.o: In function `main':
main.cpp:(.text+0x1f): undefined reference to `Popular::V3::C::print()'
collect2: error: ld returned 1 exit status

Thus, I'm wondering, is it possible to #include a namespaced class into another namespace? Or am I failing to reproduce this example for other reasons? I read in a later section (15.2.5) that it may not be possible to do this.

johnnyodonnell
  • 1,838
  • 3
  • 16
  • 34
  • You're including the header there but not the implementation. So `print()` is undefined. – Cruz Jean Apr 19 '19 at 00:41
  • Link V3.cpp also. – J.R. Apr 19 '19 at 00:42
  • @CruzJean how do I include the implementation? I thought by adding V3.cpp as an argument to g++, I would be including it. – johnnyodonnell Apr 19 '19 at 00:45
  • 1
    @J.R. Haven't I linked V3.cpp by writing `g++ main.cpp V3.cpp`? – johnnyodonnell Apr 19 '19 at 00:47
  • Sorry, I've overlooked that. – J.R. Apr 19 '19 at 01:59
  • @johnnyodonnell The impl would need to be e.g. all in the header file. Then when you `#include` the header into namespace `Popular` it'll also have the impl in that (new) namespace as well. As it stands the class def is injected into `Popular` but the impl is only for the original namespace `V3`. Needs to be in both. – Cruz Jean Apr 19 '19 at 17:33

1 Answers1

1

Yes, it's totally possible (though highly questionable). #include is really nothing more than textual copy-paste. It's as-if your Popular.h was:

namespace Popular {
    namespace V3 {
        class C {
            public:
                void print();
        };
    }
}

This happens to be legitimate C++ code, there are certainly plenty of cases where it wouldn't be.

Note that this class C is ::Popular::V3::C. That's a different, and unrelated, type from the one declared in V3.h - which is ::V3::C. The second type has a definition for its print() function in V3.cpp.

But that's not the print() you're calling - you're calling ::Popular::V3::C::print() (which is, again, a member function of a different type from ::V3::C) and there's no definition for this function anywhere. So, as a result, you get an undefined reference - you need to add a definition for this thing. Like, say:

// Popular.cpp
#include <iostream>
void Popular::V3::C::print() {
    std::cout << "This is bad and I should feel bad about it. :-(" << std::endl;
}

But really, don't #include things inside of a namespace unless you have a really strongly compelling reason to do so. You could've instead provided a namespace alias:

#include "V3.h"
namespace Popular {
    namespace V3 = ::V3;
}

This would let you still write Popular::V3::C, which is now actually the same type as ::V3::C.

Or a type alias:

#include "V3.h"
namespace Popular {
    using C = ::V3::C;
}

And here again ::Popular::C is actually the same type as ::V3::C.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • This makes sense and I was able to get my example to work. I completely understand that this is not good practice, but wanted to see how it worked anyway. Thanks for the response! – johnnyodonnell Apr 19 '19 at 00:52