Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional operator + upcast + const reference

Inspired by this question, I tried the following code:

struct A {
  virtual void doit() const = 0;
};

struct B : public A {
  virtual void doit() const;
};

struct C : public A {
  virtual void doit() const;
};

void
foo(bool p)
{
  const A &a = (p ? static_cast<const A &>(B()) : static_cast<const A &>(C()));
  a.doit();
}

Every compiler I have tried accepts this code with -Wall -Werror and generates the assembly I want. But after carefully reading the C++03 specification section 12.2 ("Temporaries") and section 5.12 ("Conditional Operator"), I am unsure whether this is guaranteed to work.

So, is this valid code, or does it invoke undefined behavior? Does the answer differ for C++03 and C++11?

Citations from relevant specifications would be appreciated.

like image 510
Nemo Avatar asked Apr 14 '14 00:04

Nemo


1 Answers

Oh, it's very invalid.

Consider:

#include <iostream>
using namespace std;

struct A {
    virtual ~A() { cout << "~A" << endl; }
    virtual void doit() const = 0;
};

struct B : public A
{
    ~B() override { cout << "~B" << endl; }
    void doit() const override { cout << "A::doit" << endl; }
};

struct C : public A
{
    ~C() override { cout << "~C" << endl; }
    virtual void doit() const { cout << "C::doit" << endl; }
};

void foo(bool p)
{
    cout << "foo( " << p << ")" << endl;
    const A &a = (p ? static_cast<const A &>(B()) : static_cast<const A &>(C()));
    a.doit();
}

auto main( int argc, char* argv[] ) -> int
{
    cout << boolalpha;

    foo( true );
    cout << endl;
    foo( false );
}

Output in Coliru Viewer, using g++ 4.8:

foo( true)

~B

~A

pure virtual method called

terminate called without an active exception

bash: line 7: 16922 Aborted                 (core dumped) ./a.out

It's UB so any explanation could be true, but one can be reasonably sure, without looking at the assembly, that what happens is:

  • A temporary is constructed.
  • It's bound to the reference.
    This is a reference being bound to a reference, so does not involve creation of a new temporary or slice.
  • The temporary is destroyed.
  • As part of that its dynamic type (vtable pointer) is changed to A, which is abstract.
  • The pure virtual in A is called.
like image 80
Cheers and hth. - Alf Avatar answered Oct 31 '22 15:10

Cheers and hth. - Alf