3Dave
3Dave

Reputation: 29041

Interfaces in C++, WITHOUT multiple inheritance

I suspect that the answer to this is "no" or "you're doing it wrong," but:

Is it possible to implement interface-type behavior WITHOUT using inheritance in C++ (11, if it matters)?

I have a couple of different structs,

struct Foo
{
  float A;
  void Bind() 
  { .... }
};

struct Bar
{
  float B;
  void Bind()
  {
  }
};

... and others

These are operated on by a method that passes arrays of these structs to another process, and they have to be pretty tightly packed. If I use inheritance, creating a base class that implements the ::Bind() method, then the descendent classes have not only their data, but a VMT, which consumes a significant chunk of a very scarce resource. Other methods need to operate on these different types, but don't really care about the data members or the specifics of the ::Bind() method, which differs greatly between types.

In C# (or, I suspect, java), I'd do something like:

interface ICommon
{
  void Bind();
}

struct Foo : ICommon
{
  void Bind() { .... };
};

struct Bar : ICommon
{
  void Bind() { ..... }
}

I could use a template:

template<typename T>
void Bind(T &item)
{
  item.Bind();
}

but this introduces some constraint (ie, template needs to be declared in a header rather than a cpp, etc.) that are less than ideal. I'm aware of some hacks that let you put a template method implementation in the cpp file, but they're kind of messy and I'd rather avoid it.

This may be a "have your cake and eat it, too" request.

(Note that this isn't really a duplicate of other C++ Interfaces questions as I'm trying to avoid the oft-recommended solution of using multiple inheritance.)

Upvotes: 1

Views: 830

Answers (4)

vonbrand
vonbrand

Reputation: 11791

Java's interfaces are just a watered down way of doing evil, evil, cross my heart, we won't ever do that, multiple inheritance. Nothing more.

For your problem, if you want to get a bunch of objects that share an "interface," do as is natural: They belong to the interface's class, i.e., are derived from it. Can create an array of (pointers to) such objects, with a little care even of the objects themselves (but I wouldn't go there unless absolutely necessary, the danger of slicing off something is just too great).

Re: "templates only in headers": Says who? Headers are just included (probably in several different source files), in order to avoid writing the same declarations (and inline definitions) over and over. If you need templates, or classes, or whathaveyou just in a single source file, noboby will force you to create a header just for that.

Upvotes: 0

ebasconp
ebasconp

Reputation: 1638

You can achieve almost the same result using template parameters:

template <typename TRAIT>
class ICommon
{
  TRAIT t;

  public: void Bind()
  {
    t.Bind();
  }
}

class FooTrait
{
  public: void Bind() { .... };
};

class BarTrait
{
  public void Bind() { ..... }
}

typedef ICommon<FooTrait> Foo;
typedef ICommon<BarTrait> Bar;

template <typename T>
void call_bind(ICommon<T> x)
{
  x.Bind();
}

int main()
{
  Foo f; Bar b;
  call_bind(f);
  call_bind(b);
}

Upvotes: 1

eerorika
eerorika

Reputation: 238311

If you really don't want to use templates or inheritance, you could use overloaded free functions:

void Bind(Foo& foo) {}
void Bind(Bar& bar) {}

int main() {
    Foo foo;
    Bar bar;
    Bind(foo);
    Bind(bar);
}

Of course, any function that needs to operate on either type must be either overloaded or templated.

Upvotes: 1

utnapistim
utnapistim

Reputation: 27365

Is it possible to implement interface-type behavior WITHOUT using inheritance in C++ (11, if it matters)?

Yes. Encapsulation is a viable alternative to inheritance.

You use the interfaces to define some behavior, then return the interface (the interface is still inherited, but not by your main class).

Example:

class IBinder {
    virtual void Bind() = 0;
};

class Foo: public WhateverBaseClass {
    struct Binder: public IBinder { virtual void Bind() override {} };
    Binder b;
public:
    IBinder& getBinder() { return b; }
};

Client code:

Foo f;
f.getBinder().Bind();

Upvotes: 1

Related Questions