-4

I have a base class and a derived class along with a function that returns a reference to the base class. Is it safe to use make_unique and then downcast the pointers?

I am trying to avoid a copy operation.

class Animal {}
class Dog : Animal {}

Animal GetAnimal() { ... }

Dog GetDog() {
   Dog dog = *std::make_unique<Dog>( GetAnimal() );
   return dog;
}

Or is there a more straightforward way?

EDIT:

Here is the actual code (which is pretty close to what I am showing above:

// Convert from (m)anaged to (u)nmanaged Title 
Title Data::MarshalTitle(TitleMap ^mdefn) {
    Title udefn = MarshalValue(mdefn);
    return udefn;
}

and then MarshalValue is defined as:

Value Data::MarshalValue(TitleMap ^mdefn)

Now, what you don't see here is that Value is a base class and Title is a derived class.

The error I get from the compiler is:

error C2440: 'initializing' : cannot convert from 'Definitions::Value' to 'Definitions::Title'  D:\Projects\Parsers\View.cpp

Intellisense tells me that there is no suitable user-defined conversion from Value to Title.

This however, gets through the compiler fine, but I am unsure if this is safe.

// Convert from (m)anaged to (u)nmanaged Title Dimension definition
Title Data::MarshalTitle(TitleMap ^mdefn) {
    Title udefn = *std::make_unique<Title>(MarshalValue(mdefn));
    return udefn;
}
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
user3072517
  • 513
  • 1
  • 7
  • 21
  • 2
    `GetAnimal()` returns an `Animal` by value, so I don't see how you can construct a `unique_ptr` from that. And `make_unique` is the wrong tool to use here. Please post code that actually compiles and illustrates your problem. – Praetorian Feb 05 '15 at 20:38
  • I think you have typos : is ^ supposed to mean & ? and Data::MarshalValue is returning a Value in its declaration and returns a Title in its definition.... – Bérenger Feb 05 '15 at 23:21
  • No, ^ is a managed class pointer reference. This code is in a C++/CLI application. As far as Data::MarshalValue, I don't have listed the code for that function, it is returning Value not Title. What is returning Title is MarshalTitle. The whole point of MarshalTitle is that it is casting from the base class of Value to the derived class of Title. The casting actually occurs in the line `Title udefn = *std::make_unique(MarshalValue(mdefn));`. – user3072517 Feb 06 '15 at 00:38

2 Answers2

0

Your problem is not the one you think you have. You are confused because your code means the inheritance is private and is equivalent to the following:

class Dog : private Animal {}

With private inheritance, your Dog is NOT an Animal.

Whereas you really want regular inheritance:

class Dog : public Animal {}

Then you actually do not need to downcast anything because C++ will do it for you.

With public inheritance, the following will work without any copy:

void myFun(Animal&);
int main() {
    Dog dog;
    myFun(dog);
}
Bérenger
  • 2,678
  • 2
  • 21
  • 42
  • I do actually have it defined as `class Dog : public Animal {}`. I missed that when I typed it in. But, if I don't go through the make_unique, I get the error: No suitable user-defined conversion. Which tells me it is looking for a constructor and trying to copy by value rather than downcasting. – user3072517 Feb 05 '15 at 20:51
  • Ok. In GetDog(), you actually make a copy in the first line. Can you provide a piece of code that triggers the error you are talking about ? – Bérenger Feb 05 '15 at 21:20
0

Ok, I found the answer to the original question. The short of it is: it is NOT safe to do this. It circumvents the casting evaluation and it will blow a gasket at runtime.

What the actual problem was turned out to be obscured because of the number of nested derived classes, and one of them in the middle, actually did not have a downcasting constructor.

Effectively, it turned out to be something like this:

class A() {}
class AA() : public A {}
class AAA() : public AA {}
class AAAA() : public AAA {}

Each of these classes had a definition something along the lines of:

class AAAA() : public AAA {
    AAAA();
    virtual ~AAAA()

    // Downcasting constructor
    AAAA( const AAA &base ) : AAA(base) {};
}

In the class AAA(), however, the downcasting constructor to AA was missing, but all the compiler could report back was that it could not find a suitable constructor for AAAA( AAA ).

And indeed, there was a break in the chain, so it couldn't get to anything at the level of AA or above (which is what I was attempting to get to).

Lesson learned: When building a deeply derived chain of objects, always double check to make sure the downcasting constructor is there for each and every class, and make sure that it is public.

In another instance of a different set of classes, it was converted from a struct to a class, and Public: was forgotten. In this case, you don't get an inaccessible warning, you just get a cast failure. Everything looks ok, but the chain is broken.

Perhaps this might save someone else time who is running into a similar problem.

user3072517
  • 513
  • 1
  • 7
  • 21