Search code examples
c++language-design

User-defined conversion operator to inaccessible base class


Why not allow user-defined conversion to base class (or reference to it) when base is inacessible: protected or private?

When there is a class D and its public base B, there is an implicit rule to bind references to B (B& or B&&, possibly cv-qualified) to objects of the class D, so the user-defined conversion to B& doesn't make sense. But when the base class is protected or private, the implicit rule is not applicable anymore. So why not allow to use user-defined conversion to B& (or const B& or B&& etc.)?


Solution

  • It is allowed, nothing in the Standard prohibits this. But it just states that such a conversion operator will never be used. [class.conv.fct]/1:

    A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void.

    Overload resolution will always prefer the base class constructor over a conversion operator, and the conversion operator will never be called, so it is unnecessary for implicit conversions. Access checking is always done after overload resolution, so the the conversion operator is never considered.

    struct B {
        B() = default;
        B(const B&) = default;
        B& operator=(const B&) = default;
    };
    
    struct D : protected B {
        operator B() { return *this; }
    };
    
    int main() {
        D d;
        B b = d; // (1)
        b = d; // (2)
    }
    

    For (1), the copy constructor B(const B&) is a better match then converting D to B using a conversion operator ([over.match.ctor]/1), so that constructor will be chosen. But only now is access checked, and because the B's copy constructor is protected, it doesn't compile.

    For (2) almost the exact same thing. B& operator=(const B&) is chosen by overload resolution because it is a better match than to call the user defined conversion operator of D. But now B's assignment operator is also protected, and so you can't access it outside of D, and your code doesn't compile.

    That's just how overload resolution works, as far as I know that is the only reason.