sh-
sh-

Reputation: 1031

Is there a way to bring all definitions in a C++ struct/class into scope without deriving from it?

Musing on a Sunday...

Deriving from a class brings all names from the base class into the scope of the derived class. However, it also adds the base class non-static data members to every instance of the derived class.

Is there a way to achieve the former without the latter? I'm asking in the interest of concise notation.

Obviously, when the base class doesn't have any data members, I get what I want. There are quite a few empty classes or class templates in the standard library defined to do just that - inject names summarily into a class scope through inheritance. There's even the empty base class optimization to make this as cheap as possible.

But if I wanted to do the same with a non-empty base class, I would be tempted to employ something like:

struct Bar {
    using struct Foo;
};

But, alas, that's not supported by C++. My question is, is there another way to achieve the same which I overlooked?

To provide a more complete example:

struct Foo {
    enum { some_constant=42 };

    // data members follow here ...
};

struct Bar {
    using class Foo;   // this doesn't compile

    int f();
};

int Bar::f() {
    return some_constant;   // I want to use the constant directly, without Foo::
}

One clumsy way could be to split the definitions in Foo into two classes, one with the constants (which would be an empty class I could derive from without penalty) and the other with the data members, but that looks rather like an inelegant hack to me.

If there isn't a clean way to achieve this, maybe someone can provide a rationale for why it doesn't exist, or perhaps shouldn't exist.

Upvotes: 1

Views: 334

Answers (2)

Nicol Bolas
Nicol Bolas

Reputation: 473577

Deriving from a class brings all names from the base class into the scope of the derived class.

Let me stop you there. Yes, it is true that inheriting from a base class causes the (non-private) names in the base class to be accessible from the derived class definition. However, that's not why you inherit from a base class; that's merely the mechanism by which inheritance achieves its goal.

To publicly inherit from a base class is to make a statement about the relationship between the derived and base classes. You're saying that every instance of the derived class should behave like the base class in virtually all ways. Even virtual function overriding still carries with it the expectation that the derived class implementations of these methods are conceptually doing the same job, just in a way appropriate for that derived class.

This is true even of mixin-style base classes, where the base class is used to define common functionality that is imported into a particular derived class. In such interfaces, there is little expectation of a user explicitly talking to base class definitions. But this provision of common functionality is ultimately still based on the semantic idea of a derived class being a base class. And that's very important for many of them to do their job.

Consider what is probably the most prominent mixin in the C++ standard library: std::enable_shared_from_this<T>. It has non-static data members, without which it couldn't actually provide the functionality it does (well, it could, but you would have to provide some interface in your derived class to store them, so it may as well do it).

This is true of private inheritance, though there is some modification. While to the outside world, the derived class is just a derived class, to the class definition itself, it still remains a base class. It remains wholly a base class, along with all the baggage that comes along with it.

Do not mistake the mechanism for the meaning. Mechanisms are important; don't get me wrong. But those mechanisms exist to facilitate meaning.

Having a class contain everything of some other class except the non-static data members is, semantically, nonsense. It doesn't mean anything about the relationship between the types. And you've essentially admitted that the main reason you want this is so that you don't have to scope-qualify the names defined in the "base" class.

This is a mechanical reason, not a semantic one. You shouldn't employ a semantic tool like inheritance to escape the mechanical consequences of how you have chosen to design your types.

Upvotes: 2

castro
castro

Reputation: 429

In reference to your specific example you could make the constants you want to access static, which will allow you to access them from the second class by fully qualifying with the "base" class

Upvotes: 1

Related Questions