-2

I'm trying to call a parent constructor, with a given pointer to sibling object:

class Base{
public:
    Base(const Base&) =default;    
};

#include "daughter.h"     // <-- problem! I'll come to this in a second.

class Son: public Base{
public:
    Son(Daughter* d) : Base(*d){};
};

But (and here comes the problem), this relationship goes in both directions:

// daughter.h
class Son;               // forward declare needed
class Daughter: public Base{
public:
    Daughter(Son* s) : Base(*d){};
};

Uh-oh: (link to run)

error: no matching function for call to 'Daughter::Daughter(Base&)'

note: candidates are:

note: Daughter::Daughter(const Base&)

note: no known conversion for argument 1 from 'Daughter' to 'const Base&'

So, the issue arises since at that point - where Son is an incomplete type - it is not known that it inherits from Base, and so this constructor doesn't match.

I have been able to 'solve' it:

Daughter::Daughter(Son* s) : Base( *reinterpret_cast<Base>(*s) ){};

But this seems bad practice - I don't think I should need to reinterpret_cast for something of such innocent intent - we all know Son will be a derived class of Base!

Is there a better way of dealing with this?

NB: While possibly true that it comes from bad overall design - "[I] shouldn't need to construct a parent from a sibling." - please bear in mind that I have reduced this to a very minimal example from a much larger design (with many more siblings, and many more constructors for each) in which doing this is necessary in a couple of places.

Community
  • 1
  • 1
OJFord
  • 10,522
  • 8
  • 64
  • 98
  • You need to use `dynamic_cast`, not `reinterpret_cast`! – Emil Laine Mar 21 '15 at 17:56
  • @zenith I did try that before jumping to `reinterpret_cast`. Error because it's a[edit: pointer to a]n incomplete type. – OJFord Mar 21 '15 at 17:57
  • So is your `Base` constructor `Base(Base&)`? Please include it in the question. – Emil Laine Mar 21 '15 at 18:09
  • Also, why are your `Daughter` and `Son` constructors taking a pointer if you just dereference it without checking for null? – Emil Laine Mar 21 '15 at 18:11
  • @zenith `Base::Base(const Base&)`, i.e. the default copy constructor. I didn't include it in the question because it's not mine; it's not in my source. They're pointers because this is a minimal example, where I've stripped any reason for them to be pointers, but if I change them from pointers then it's no longer a correct question. – OJFord Mar 21 '15 at 18:11

2 Answers2

1

Your classes are circularly dependent. If you move the definition behind the definitions of the both classes, you can fix the "incomplete class" problem.

Note that the definitions must be defined only once, or defined inline.

#include <string>
#include <iostream>
#include <list>
#include <vector>

class Base{
public:
    Base(const Base&) =default;
};

class Son;
class Daughter: public Base{
public:
    Daughter(Son* s);
};

class Son: public Base{
public:
    Son(Daughter* d);
};

Son::Son(Daughter* d) : Base(*d){};
Daughter::Daughter(Son* s) : Base(*s){};

int main(){
}

(this doesn't fix the other issues though - the design is pretty WTF)

milleniumbug
  • 15,379
  • 3
  • 47
  • 71
  • Whoops, they're not actually, that's just a mistake in question. Fixing. – OJFord Mar 21 '15 at 18:05
  • @OllieFord Post the code that actually exhibits the problem, and doesn't have any other problems except for this one. – milleniumbug Mar 21 '15 at 18:07
  • No. If I had done that initially you (and if I do it now, someone else) will tell me not to code dump, and produce an MCVE, which I've done. – OJFord Mar 21 '15 at 18:09
  • 1
    @OllieFord I didn't ask for code dump. I asked for [MCVE](http://stackoverflow.com/help/mcve). I can't copy it directly to my editor and compile - it won't display the same errors you show - it's not Complete. It's not Verifiable, because it has other problems, like missing semicolon at the end of class definition. – milleniumbug Mar 21 '15 at 18:13
  • Okay, fair enough. I added [this link](http://coliru.stacked-crooked.com/a/caeb43c05f5caa2b) to question, fixed that typo, and added the default constructor explicitly (my compiler didn't need it, but coliru did, so I figured that helps). – OJFord Mar 21 '15 at 18:17
  • What are the unmentioned other issues? – OJFord Mar 21 '15 at 18:23
  • @OllieFord The other issues are related to the design - but that's something can't elaborate on, since you didn't mention the details. – milleniumbug Mar 21 '15 at 18:26
  • Oh okay. I didn't mention them because I don't have time to uphaul the design (I tried to acknowledge that I know it's probably needed) - and the more I elaborate the more it's going to have that homework smell (because it is!), which is the other reason I wanted to keep it as general as possible. I spent a lot of time considering a good way to design it in order to avoid other problems, dodged some but not all it seems. – OJFord Mar 21 '15 at 18:29
1

You have to define the constructors in cpp files to break the circular dependency:

// base.h
class Base {
public:
    Base(const Base& b) { ... }
};

// son.h
#include "base.h" // include because you need the definition for inheritance
class Daughter;   // don't include, just forward declare
class Son: public Base {
public:
    Son(Daughter* d);
};

// son.cpp
#include "son.h"
#include "daughter.h"
Son::Son(Daughter* d) : Base(*d) {}

// daughter.h
#include "base.h" // include because you need the definition for inheritance
class Son;        // forward declare needed
class Daughter: public Base {
public:
    Daughter(Son* s);
};

// daughter.cpp
#include "daughter.h"
#include "son.h"
Daughter::Daughter(Son* s) : Base(*s) {}
Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • Agh that's so stupid of me. Is the `dynamic_cast` still needed though? – OJFord Mar 21 '15 at 18:22
  • @OllieFord It isn't. The upcasts are a no-op. – milleniumbug Mar 21 '15 at 18:23
  • @milleniumbug As in, `dynamic_cast` recognises it's not necessary, and does nothing? Is it better practice to write or omit then - since I thought it was to be avoided (I asked the question because of same view - more so - of `reinterpret_cast`). – OJFord Mar 21 '15 at 18:26
  • @OllieFord Good point. Removed them. And yeah, upcasting = no cast, downcasting = dynamic_cast. – Emil Laine Mar 21 '15 at 18:26