1

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.)

3Dave
  • 28,657
  • 18
  • 88
  • 151
  • 2
    An interface without dynamic dispatch is like a fish without water. – Hans Passant Mar 12 '14 at 14:32
  • Even w/o the Bind() problem, how would you have array of these different structs without a common base class ? – GabiMe Mar 12 '14 at 14:34
  • Do you actually need dynamic dispatch? If not, a template is your best option. I don't understand what's your problem with putting methods in header files, but there might be a solution to this as well. – pmr Mar 12 '14 at 14:37
  • "descendent classes have not only their data, but a VMT" This may be nitpicking, but descendant classes don't have the (non-static) data. *Instances* of the descendant classes have data - and a vptr to the vmt. – eerorika Mar 12 '14 at 14:40
  • I'd go with the template approach. It's not _that_ messy if you put it into `cpp` files. You can keep compile times down with `extern template` as well. – stefan Mar 12 '14 at 14:45
  • http://stackoverflow.com/a/16648036/412080 answers your question. – Maxim Egorushkin Mar 12 '14 at 14:57
  • Who (what code) needs to operate on both `A` and `B` in a uniform manner? Who needs arrays of `A` or `B`? Who needs to descend from `A` or `B`? Why do you think that the `C#` or `Java` solution involving interfaces would be any better than having a VMT in C++? Who is calling `Bind`? Why is a 1 line `template` function in a header file a problem? – Yakk - Adam Nevraumont Mar 12 '14 at 14:59

4 Answers4

1

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();
utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • COM does this... but what this really amounts to is implementing multiple inheritance without compiler help. Subobject for every base class? Check. Pointer adjustment moving between interfaces? Check. Support for testing whether a class implements a particular interface? COM has this essential feature that your example lacks. Support for object identity and casting back to the main object? Again provided by COM without multiple inheritance, and lacking in your example. – Ben Voigt Mar 12 '14 at 14:52
  • OP's problem with inheritance is the memory overhead. Does this solution address that problem? Doesn't every instance of `Foo` now contain an extra `Binder` instance? – eerorika Mar 12 '14 at 14:58
  • @BenVoigt, I was (mostly) answering the question of implementing "interface-type behavior WITHOUT using inheritance". It's a bit vague as far as questions go, but pointer adjustment between interfaces was not stressed on, nor were various COM features (like runtime-checks for support, or casting back to the main object). This is simply a solution for "expose extra polymorphic behavior with at most one base class". – utnapistim Mar 12 '14 at 15:27
  • @user2079303, while my example doesn't actually solve the resources problem, that can be improved through lazy instantiation of the interface implementation. – utnapistim Mar 12 '14 at 15:29
1

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.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

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);
}
ebasconp
  • 1,608
  • 2
  • 17
  • 27
0

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.

vonbrand
  • 11,412
  • 8
  • 32
  • 52