wcochran
wcochran

Reputation: 10886

How to initialize C++ reference with different constructors based on condition?

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

Upvotes: 1

Views: 159

Answers (1)

NathanOliver
NathanOliver

Reputation: 180415

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;
}

Upvotes: 5

Related Questions