I’m puzzled with this behavior of C++:
struct A {
virtual void print() const { printf("a\n"); }
};
struct B : public A {
virtual void print() const { printf("b\n"); }
};
struct C {
operator B() { return B(); }
};
void print(const A& a) {
a.print();
}
int main() {
C c;
print(c);
}
So, the quiz is, what is the output of the program – a or b? Well, the answer is a. But why?
The problem here is a bug / misfeature / hole in the C++03 standard, with different compilers trying to patch over the problem in different ways. (This problem no longer exists in C++11 standard.)
Sections 8.5.3/5 of both standards specify how a reference is initialized. Here’s the C++03 version (the list numbering is mine):
There are three types involved in the question at hand:
T1. In this case, it isstruct A.T2. In this case, the initializer expression is the variablec, soT2isstruct C. Note that becausestruct Ais not reference-compatible withstruct C, it’s not possible to directly bind the reference toc. An intermediate is needed.T3. In this case, this isstruct B. Note that applying the conversion operatorC::operator B()tocwill convert the lvaluecto an rvalue.The initializations by what I labeled as 1.1 and 3 are out because the
struct Ais not reference-compatible withstruct C. The conversion operatorC::operator B()needs to be used. 1.2 is out Because this conversion operator returns an rvalue, this rules 1.2 out. All that is left is option 4, create a temporary of typecv1 T1. Strict compliance with the 2003 version of the standard forces the creation of two temporaries for this problem, even though only one will suffice.The 2011 version of the standard fixes the problem by replacing option 3 with
It appears that the gcc family of compilers chose strict compliance over intent (avoid creating unnecessary temporaries), while the other compilers that print “b” chose intent / corrections to the standard. Choosing strict compliance isn’t necessarily commendable; there are other bugs/misfeatures in the 2003 version of the standard (e.g.,
std::set) where the gcc family chose sanity over strict compliance.