4

Would there be a danger of slicing

result Compare(const Osp::Base::Object &obj1, const Osp::Base::Object &obj2, int &cmp) const {
    cmp = ((const Block)obj1).NumSuperBlocks() - ((const Block)obj2).NumSuperBlocks();
}

Where

class Block : Object {/*Filler*/}

and obj1 and obj2 are assured to be Block objects?

I'm tempted to use:

    cmp = ((const Block*)&obj1)->NumSuperBlocks() - ((const Block*)&obj2)->NumSuperBlocks();

but on reading SO's brief description of the object-slicing tag I'm tempted to use the former. But I really don't want any nasty silent slicing.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
John
  • 6,433
  • 7
  • 47
  • 82

2 Answers2

11

References and pointers are both polymorphic.

You may prefer

static_cast<const Block&>(obj1).NumSuperBlocks()

for downcasting starting with a reference, it's equivalent to *static_cast<const Block*>(&obj1).

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    `const Block&`, as function argument is `const` – Lol4t0 Feb 14 '12 at 19:32
  • @BenVoigt: So pointers and objects would make no difference to the liklihood of slicing here? TBH, the former with no *& feels more readable and natural. – John Feb 14 '12 at 19:33
  • @John: No difference between pointer and reference here. I don't like your original code; it may slice. It also requires an accessible copy constructor, and operates on a copy. Use a reference or pointer here. – Ben Voigt Feb 14 '12 at 19:34
  • @Lol4t0: Thanks. I had looked at the pointer code in the question (no `const`) instead of the actual argument type. – Ben Voigt Feb 14 '12 at 19:35
  • @BenVoigt: Isn't slicing supposed to only happen with cast going the other way? – John Feb 14 '12 at 19:36
  • Consider using `dynamic_cast` to ensure that the conversion is valid (if you have at least one virtual function in the base type) – David Rodríguez - dribeas Feb 14 '12 at 19:37
  • @John: slicing happens whenever the converted type is not the final type. In your case, even if you are downcasting, if the type is not `Block` but rather derived from `Block` you will slice. – David Rodríguez - dribeas Feb 14 '12 at 19:38
  • @DavidRodríguez-dribeas dynamic_cast is slooow, and I don't have v-tables. Ben, the objects will all be of the final type Block, or derived from it. Only just extended `Block`, looks like it will be getting a new wrapper, thanks. – John Feb 14 '12 at 19:48
  • @John: so you have no virtual functions, you downcast all the time and even if you could `dynamic_cast` would be slow... but you make extensive use of inheritance? Either you are in the few percentile were the difference really matters, or you have misconceptions that have led you to a flaky design where it suddenly matter... – David Rodríguez - dribeas Feb 14 '12 at 20:16
1

First of all don't use C-style casts for downcasting (or any casting at all). It's very dangerous because it circumvents all compiler checking.

That said, you don't need to worry about slicing when you are downcasting references or pointers.

For polymorphic objects (i.e. objects that have virtual methods) you can use dynamic cast, which gives you compile-time + runtime checking (returns null when downcasting a pointer to the wrong type, or throws a bad_cast exception when downcasting a reference to the wrong type):

Block & block = dynamic_cast<Block&>(obj);

For non polymorphic objects you can use static_cast.

StackedCrooked
  • 34,653
  • 44
  • 154
  • 278
  • 1
    A C-style cast will act like a `static_cast` when the types are related (as they are here), and a `reinterpret_cast` otherwise. So the compiler will take care of the offset. Also, `dynamic_cast` is only legal if `Object` has `virtual` member functions. – Ben Voigt Feb 14 '12 at 19:32
  • If you use `dynamic_cast` be sure to check for a result of `NULL`. – Mark Ransom Feb 14 '12 at 19:37
  • 1
    @Lol4t0, somewhere in the back of my brain I knew that. I guess I've been using dynamic_cast on pointers too much. Thanks. – Mark Ransom Feb 14 '12 at 19:42
  • @MarkRansom but checking for null is only possible with pointers, right? So for references one needs to check for an exception. – Sebastian Oct 25 '13 at 14:07
  • @SebastianDressler, you're absolutely correct. With pointers check for NULL, with references catch the exception. It looks like there's a deleted comment that made the same point, because I can see my reply to it. – Mark Ransom Oct 25 '13 at 15:40