1

The reference variable foo below is initialized with either an instance of Foo or its derived class Bar based on condition. Strangely enough, based on the output of the say() method, foo seems to be an instance of Foo rather than an instance of Bar — why?

#include <iostream>

class Foo {
public:
    virtual void say() const {
        std::cout << "Foo\n";
    }
};

class Bar : public Foo {
public:
    virtual void say() const {
        std::cout << "Bar\n";
    }
};

int main() {
    constexpr bool condition = false;
    const Foo& foo = condition ? Foo() : Bar();
    foo.say();   // outputs "Foo” ???                                                                                                  
    return 0;
}

If I annotate each constructor I can see that the Bar constructor is getting invoked when evaluating the ternary expression. If I annotate each destructor I see that Bar destructor is invoked before foo is initialized — this tells me that a temporary Bar object is created by the ternary operator, but is destroyed before initialization — why?

Compiled with (Apple LLVM version 9.0.0 (clang-900.0.39.2))

clang++ -Wall -std=c++11 foo.cpp -o foo

1 Answer 1

5

The problem with

const Foo& foo = condition ? Foo() : Bar();

is that both parts need to return the same type. Since Foo() and Bar() aren't the same type, the compiler tries to convert them. The only valid conversion it can do is to slice Bar() into its Foo part. This means no matter what you get, you'll be binding a reference to a Foo and the Bar part disappears.

To fix this you'll need to use pointers like

#include <iostream>
#include <memory>

class Foo {
public:
    virtual ~Foo() = default;  // don't forget to add this when using polymorphism
    virtual void say() const {
        std::cout << "Foo\n";
    }
};

class Bar : public Foo {
public:
    virtual void say() const {
        std::cout << "Bar\n";
    }
};

int main() {
    constexpr bool condition = false;
    auto foo = condition ? std::make_unique<Foo>() : std::make_unique<Bar>();
    foo->say();   // outputs "Bar" now                                                                                                  
    return 0;
}
Sign up to request clarification or add additional context in comments.

2 Comments

Excellent. Bonus: using unique ptrs saves me from having to explicitly delete as well.
@wcochran Yeah, not having to write new and delete is really nice.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.