0

I have several classes D with public sections of the form:

class D
{
    public:
        D& foo();
        void bar(D&);
}

I'd like to create a single abstract class from which they all derive.

My (naive) attempt was:

// in .h file
class B
{
    public:
        virtual B& foo() = 0;
        virtual void bar(B&) = 0;
}

class D : public B
{
    public:
        D& foo() override;
        void bar(D&) override;
}

// in .cpp file
D& D::bar() {return *(new D());}
void D::foo(D& d) {}

This failed to compile for (what I eventually realized was) a fairly sensible reason: Any function overriding the function

void bar(B&)=0;

must be defined for any parameter which is a reference to type B. The supplied candidate

virtual void bar(D&) override;

is only defined for (the smaller collection) of parameters which are references to type D.

Note that this is not a problem with the function foo. Indeed, if you comment out the three lines with bar, everything compiles fine.

I think that the technical explanation for this phenomenon is that C++ does not support covariance in parameters (but it does support contravariance in parameters).

The answer to the post C++ covariance in parameters suggests that I can't define an interface (i.e. an abstract class) for my classes D.

Is there some simple or conventional way to create a single "interface" for all my classes D? Alternatively, perhaps there is a different design pattern for hiding the different implementations of these classes.

Thanks in advance for your comments and suggestions.

dan

Community
  • 1
  • 1
sitiposit
  • 149
  • 1
  • 13

3 Answers3

1

You can't, and for good reason. A derived class can't add preconditions that are more restrictive than the interface it derives from without breaking every principle of OOP that exists. By requiring the parameter to be more specific in your implementation of the interface this is exactly what you are doing.

An argument could be made that something like this could be useful:

struct IfaceA {};
struct IfaceB : IfaceA {};

struct Base { void f(IfaceB &); };
struct Derived : Base { void f(IfaceA &); };

This lessens preconditions rather than increase them, so it's OK. It's simply not done in C++, or any other language I'm aware of for that matter, so you just can't do it.

In both cases you can make an overload with the alternative parameter type and call the overridden version.

It's the opposite case with return types. Return values are post-conditions. You can make post conditions more specific, but can't make them more broad. So you can return your derived type but can't broaden it by returning a more abstract type. C++ implements covariant returns though at least one, very commonly used compiler does it very badly so that there are numerous bugs.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
0

Based on the code that you have provided you have tried to override two diferent function signatures. The best option you have is use the same signature, and then cast the result. A simple example,

// in .h file
class B
{
    public:
        virtual B* foo() = 0;
        virtual void bar(B*) = 0;
};

class D : public B
{
    public:
        B* foo() override;
        void bar(B*) override;
};

// in .cpp file
B* D::foo() 
{
    D* p=new D();
    return p;
}
void D::bar(B* d) 
{
    D* casted=dynamic_cast<D*>(d);
}


int main(void)
{
    D son;
    B* father=dynamic_cast<B*>(son.foo());
}

I hope that this can solve your problem, or at least give you a clue.

  • This seems like it would work, but I'm always a little suspicious about the wisdom of using dynamic_casts. I imagine that there are situations where a cast is appropriate, but I generally try to avoid them. Are you sure this is solution conforms with the recently published c++ guidelines? – sitiposit Mar 23 '16 at 01:03
  • I am also against dynamic_cast, the cost of checking the virtual table is too high, I you know what you are doing you should always use static_cast instead. about the c++ guidelines, I have to admit that I've studied c/c++ quite old fashion way, but, I you could give me any advice/source I will be grateful. – Asier Sánchez Rodríguez Mar 23 '16 at 09:57
0

Could you use a templated base class?

template <typename Deriving>
struct Base {
    virtual Deriving& foo() = 0;
    virtual void bar(Deriving&) = 0;
};

struct Deriving : public Base<Deriving> {
    ...
};

You don't have a single interface class, but there is a single interface template, which is sometimes versatile enough.

joelw
  • 401
  • 3
  • 11
  • This seems like what I was looking for. The problem I posed seems like a pretty natural one. Is this solution a standard/common way to solve it? I'm trying to be careful to keep good coding practices and avoid "hacks". – sitiposit Mar 23 '16 at 00:57
  • This is known as the "Curiously recurring template pattern". It's a fairly common pattern, and shouldn't raise any concerns. – joelw Mar 23 '16 at 19:19