Reputation: 10886
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
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