Reputation: 1266
The following code doesn't compile because the compiler doesn't know what foo::bar
is when it encounters it.
class foo : foo::bar {
class bar {
}
}
Is there any way to make this code (or some variation of it) compile?
Upvotes: 2
Views: 358
Reputation: 7202
If the inheritance from bar
is an implementation detail and the goal is to avoid polluting the global namespace (or the library's namespace), a common approach is to put these implementation details in a namespace named detail
. This is a convention used in libraries like Boost, for example.
namespace super_cool_library {
namespace detail {
class bar { };
}
class foo : detail::bar {
};
}
The detail
namespace is, admittedly, just a convention, but most people understand that within said namespace there be dragons.
Edits:
As Emilio Garavaglia suggests, foo
would need to befriend detail::bar
if detail::bar
should need to have the same access to foo
that a C++11 inner class would have.
Upvotes: 7
Reputation: 20730
I don't know why, but I find this question very interesting. Especially seeing how many answer try not to give a technical reason why the language don't allow this, but try to convince the author about the "ugliness" of such a definition (tha's ultimately a subjective matter).
Although the form of the question looks unconventional, what is asking is perfectly linear: since recursion is one of the primitives behind certain programming paradigms (that C++ claim to support, like the functional paradigm), if functions are allowed to be recursive (either directly or indirectly) why the same cannot apply to data definitions?
After all, if what is asked is possible a universe of interesting features opens in the area of meta-programming: when a generic traits class is used as a type generator for a generic class defining generic algorithms on top of a generic state, having a constructor like that can allow (among other things) to place the type generator into the generic class itself, simplifying the overall parametrization, leading to a completly different way to approach the generic algorithms and their "progressive partial specialization".
The rereason why this is not possible, is that C++ compilers, have to support independent translation units (that links after compilation), and have to support a non-orthogonal grammar (this is know as the "undecidable syntax problem").
Because of those two facts together, C++ cannot have a pure recursive global parser. You cannot use "unknown symbols" letting the association of that symbol to something concrete to happen later because the way to interpret the expression that symbol appears in, depends on the nature of that symbol itself: a<b>c
is declaration of a variable, or an expression involving variables? You cannot know if you don't know if a and b are variables or types.
And since the "nature of a symbol" is "compiler internal state", you cannot use foo::bar
as it is a class (like as a base to form a derived one) before knowing it really IS a class (and not a variable or something else). But this happens only later (when the class bar declaration is met).
This is the same reason that lead to have forward declarations, and declaration separated from definitions: a(b)
cannot be transalted if at least a
is not known to be a "callable object". If a
is function, at least its prototype must be known when a(b)
is met.
But in your case there is no way to anticipate the foo::bar
declaration being the bar declaration to stay inside the foo definition.
To come to a solution the language should support someting like
1: class foo; //we know foo is a class
2: class foo::bar; // we know bar is a class inside foo scope
3: class foo::bar
{
...
}; //we know here how wide foo::bar is
4: class foo: public foo::bar // we must know about foo::bar width here...
{
...
}; //... to sum up the foo size.
The problem, here, is that 2:
is not legal C++: the compiler has not yet seen the bar
symbol alone, with the word class
before it.
But this requires
class foo
{
class bar;
};
to already exist. But if you make that to exist you cannot anymore declare class foo: public bar {...};
later.
In other words, you meet an non-orthogonality in the grammar. The point is that it will not be solved, since around this inconsistency there have been deployed a number of programming paradigms that does not require that construct to work, and have already make enough brainwashing to let sooo many people (just look at the comments in you question and under the most of the answers) the let them to just refuse the idea even before trying to understand if it can be useful to something. They just say "I don't like that something, since I just have something else to like, and that can disturb me. And try to find false metaphors to support motivations that -ultimately- reside in the old C compilation model C++ still has to deal with.
Upvotes: 5
Reputation: 16824
I have no idea why you'd want to do this, but if it's tricks you're looking for, CRTP-like things are always fun:
template <typename T>
struct Foo : public T
{
struct Bar
{
};
};
struct empty {};
int main()
{
typedef Foo<empty>::Bar Bar;
Foo<Bar> f;
}
Upvotes: 0
Reputation: 73061
I don't think C++ supports what you want, but if the goal is to avoid polluting the global namespace with "bar", how about putting bar into a separate namespace? E.g.
namespace shums_private_namespace {
class bar {
};
};
class foo : shums_private_namespace::bar {
public:
foo() {/* empty */}
};
Upvotes: 0
Reputation: 9733
you can however do this:
struct A
{
struct B
{
};
};
struct C : public A::B
{
};
Upvotes: 1
Reputation: 105886
It's not possible to have the outer class inherit from a nested class. It also doesn't make that much sense if you think about it. A class derives from its internal subclass? That's like a snake eating its own tail.
More likely you want both to share common methods. In this case you can create another class which provides a common interface/base for foo
and foo::bar
.
class common_base{ /* ... "shared" content ... */ };
class foo : public common_base{
class bar : public common_base {
/* depending on what you want to do this can be empty */
}
}
Upvotes: 3