0

Consider the code:

#include <iostream>

class base {
};

class derived_a : public base {
};

class derived_b : public base {
};

int main() {
    base A(derived_a());  // seems to work
    base B(derived_b());  // also good

    base C( true ? derived_a() : derived_b());  // says operand types incompatible
}

Why is this not working and how do I get this working?

cpprust
  • 110
  • 4
user3600124
  • 829
  • 1
  • 7
  • 19
  • Reopened as the duplicates are not the problem – M.M Jul 21 '23 at 04:43
  • Please edit the question to contain the exact code that causes the error message . The code you have posted has some problems, including that there is no left-hand operand for the conditional operator; and it's not clear what the relevance is of the function declarations `A` and `B` – M.M Jul 21 '23 at 04:49
  • 1
    BTW, these `base A(derived_a());` and `base B(derived_b());` are not variable definitions, but function declarations. Search for "most vexing parse". – j6t Jul 21 '23 at 05:42
  • By the way there is no polymorphism there. Polymorphism requires pointers or references but there ain't any. What you have (or will have when you fix the ternary and the MVP) is *object slicing*. – n. m. could be an AI Jul 21 '23 at 06:06
  • This has nothing to do with copy constructors. The problem is exactly as described, when you use `?:` the two halves must have the same type. Add to that your `seems to work` does not work at all, because in that declaration `A` is a function. Try `base A = derived_a();` or `base A{derived_a()};` – john Jul 21 '23 at 06:32

2 Answers2

3

Supposing the real code has the expression:

true ? derived_a() : derived_b()

the problem is that the operands of the conditional operator either need to have the same type, or one has to be convertible to the type of the other. (The exact rules have some nuances but that's the gist of it).

A conditional expression (like every expression in C++) must have a type as determined by the expression itself; it's not contingent on context such as what variable you will initialize with this expression.

In this case there is no conversion from derived_a to derived_b or vice versa so the compiler can't do anything with it .

To fix this you can manually bring each operand to the intended type, e.g.

true ? static_cast<base&&>(derived_a()) : static_cast<base&&>(derived_b())

See also:

M.M
  • 138,810
  • 21
  • 208
  • 365
2

The ternary is effectively happening before the resultant object is being placed into C, so convertibility to the type of C does not come into play here.

The ternary operator requires that one of the two types (it doesn't matter which) can be unambiguously converted into the other.

But, while both those types are convertible to base, they are not convertible to each other, since they're siblings on the inheritance hierarchy.

It's no different really to the expression cond ? 42 : "42", though cond ? 42.1 : 42 would be okay since 42 can easily become a floating point 42.0.

Since you're shoehorning the result into the base class anyway, one solution is to tell the ternary that, so that it knows the types are convertible:

base C(cond
    ? static_cast<base>(derived_a())
    : static_cast<base>(derived_b())
);
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953