4

Lets go for a walk with Bulldog :)

Say I have a namespace Street::House (inside namespace Street) where the class Bulldog is declared (let it be in House/Bulldog.hpp):

namespace Street {
namespace House {
class Bulldog {};
}
}

Then, I have the Bulldog.hpp:

#include "House/Bulldog.hpp"    

namespace Street {
using House::Bulldog;
}

Pay attention to what's going on: I'm injecting declaration of Street::House::Bulldog to the namespace Street as Street::Bulldog with using declaration.

Then, I have the Owner.hpp where the class Bulldog is forward declared:

namespace Street {
class Bulldog;

class Owner {
  Bulldog* bulldog;
};
}

Finally, I have the Owner.cpp:

#include "Owner.hpp"
#include "Bulldog.hpp"

namespace Street {
// Implementation of Owner...
}

Compilation error occurs in the Owner.cpp:

error: 'Bulldog' is already declared in this scope

The natural explanation of this phenomenon seems to be that C++ treats these 2 Bulldog classes as different, but why? I can't see any ambiguity in this case, i.e. it could actually work if properly implemented by compilers.

What workarounds can you suggest? One I can think of is to simply remove forward declaration of Bulldog from Owner.hpp and move #include "Bulldog.hpp" from Owner.cpp to Owner.hpp. However, this will result in exact inclusion rather than forward declaration.

Alexander Shukaev
  • 16,674
  • 8
  • 70
  • 85

2 Answers2

3

It seems you can fix this by changing Bulldog.hpp to say

namespace Street {
    namespace House {
        class Bulldog;
    }
    using House::Bulldog;

    // ...
}

This works for me in Clang.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • Unfortunately, now error simply moved to line 5 of your example, but that was expected as actually nothing has changed. – Alexander Shukaev Mar 13 '13 at 01:04
  • @Haroogan: Really? It compiled just fine for me using Clang. – Lily Ballard Mar 13 '13 at 01:09
  • @Haroogan it works fine http://ideone.com/hGNbp1 (yes, something has changed, `using House::Bulldog` is not equivalent to declaring `Bulldog` in `Street`) – Stephen Lin Mar 13 '13 at 01:11
  • 2
    @Haroogan, your original doesn't work for the same reason [(this)](http://ideone.com/uM1Ikq) doesn't work, creating a new name for a type does not change the original name; you can't forward declare `class Bulldog` if it's actually not a class but an `using`-injected or `typedef`-ed type name. It's two different namespaces (tag names and types); C++ just lets you use a tag name as a type automatically, unlike C, but they're still distinct. – Stephen Lin Mar 13 '13 at 01:17
  • (it's not exactly the same, actually, `using` is a bit different than `typedef`, but the effect is the same in this case) – Stephen Lin Mar 13 '13 at 01:25
  • OK, I got the point. The proposed solution works. Sadly, it requires to explicitly expose from which namespace `Bulldog` comes in `Owner.hpp`. Are there other facilities (except `using` and `typedef`) in C++ to avoid this? I remember I've seen a lot of injecting code in Boost. – Alexander Shukaev Mar 13 '13 at 01:37
  • @Haroogan not really, you can obfuscate it with templates somewhat to defer name binding but each translation unit needs some way to know what namespace names are actually declared in (rather than simply injected)...otherwise the linker would have to understand a lot more C++ than it does; namespaces are basically just a way of mangling names in a consistent way – Stephen Lin Mar 13 '13 at 01:45
  • True. I'll leave it open for a few days. Maybe someone comes up with a clever trick or remembers which technique is used in Boost (because I completely forgot in which module I saw it). – Alexander Shukaev Mar 13 '13 at 01:52
  • 1
    @Haroogan: Because of the way linking works, I don't think it's possible to come up with any technique that completely eliminates the ability to tell what namespace `Bulldog` comes from by inspecting the header. – Lily Ballard Mar 13 '13 at 02:14
1

Owner.hpp which you wrote as:

namespace Street {
class Bulldog;

class Owner {
  Bulldog* bulldog;
};
}

Should have instead been

namespace Street {
namespace House {
class Bulldog;
}

class Owner {
  House::Bulldog* bulldog;
};
}

You were accidentally forward declaring Street::Bulldog, which is not a real class, instead of Street::House::Bulldog, which is a real class. Therefore, your implementation in Owner.cpp was upset because Bulldog* was ambiguous. It doesn't know if you are referring to the declared class Street::Bulldog or the declared (and also defined, though the compiler doesn't care about that) Street::House::Bulldog.

Since the class you wanted to forward declare is Street::House::Bulldog, you need to include the second, House namespace in your declaration in the .hpp file.

NHDaly
  • 7,390
  • 4
  • 40
  • 45