Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should a operator const Base&() be used for an inaccessible base class?

Tags:

c++

c++11

c++14

I would like to have a class which allows access to the const interface of it's base case, but not otherwise. In particular:

class B
{};

class A : private class B
{
public:
  operator const B&() { return *this; }
};

int main()
{
  A a;
  const B& b = a; // Should this line be an error?
}

g++ gives an inaccessible base class error. Do you language experts out there think that this error is correct in C++11/C++14?

And yes, I realize I can (and will) just do this:

int main()
{
  A a;
  const B& b = a.operator const B&();
}

Any suggestions on another method for this construct?

like image 308
BryFry Avatar asked Feb 06 '15 17:02

BryFry


People also ask

Can we overload == operator?

You cannot overload an operator working on fundamental types. That is, you can't overload the '+' operator for two int s (fundamental type) to perform subtraction. You cannot change the syntax rules (such as associativity, precedence and number of arguments) of the overloaded operator.

How do you call a base operator?

For numbers beginning with 616: dial 011-81-176-66-xxxx. For numbers beginning with 226: dial 011-81-176-77-xxxx. To call the base operator, dial 011-81-176-53-5181.

Why operator must be friends?

Since you do not have access to the stream object (its not yours to modify) these can not be member operators they have to be external to the class. Thus they must either be friends of the class or have access to a public method that will do the streaming for you.

Why do we use const in operator overloading?

The const qualifier ensures that the parameter (param) is not altered inside the operator=() method. The above method alters the right hand side operand 'param' after assigning the value to the left hand side operand. The const qualifier helps you not to violate the semantics of the assignment operation.


3 Answers

[dcl.init.ref]/5:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • If the reference is an lvalue reference and the initializer expression

    • is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or
    • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, [..]

    then the reference is bound to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).

The conversion function would be covered in the second bullet point. However, B is reference-related to (and -compatible with) A even though it's a private base class, so the first bullet point applies. Now [dcl.init.ref]/4 defines this scenario to be ill-formed:

Given types “cv1 T1” and “cv1 T2”, “cv1 T1” is reference-related to “cv1 T2” if T1 is the same type as T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if T1 is reference-related to T2 and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2. In all cases where the reference-related or reference-compatible relationship of two types is used to establish the validity of a reference binding, and T1 is a base class of T2, a program that necessitates such a binding is ill-formed if T1 is an inaccessible [..] base class of T2.

Such reference bindings will thus always fail, regardless of any conversion functions available. Reference binding cannot work with private inheritance.

Your explicit call is the solution of this problem, though there is no need for a conversion operator anymore: Simply define a getter that returns a const-reference. E.g.

const B& b = a.getB();
like image 178
Columbo Avatar answered Oct 17 '22 08:10

Columbo


const B& b = a;

doesn't invoke A::operator const B&(). This behavior exists since C++03 days.
It's a simple upcasting which happens from a derived type to a base type. This upcasting throws compiler error because the base (class B) is privately inherited by the derived (class A) in a global scope.

Had there been no such inheritance relationship between B and A, then definitely the mentioned operator const B&() would have been instantiated as per your expectation.

like image 34
iammilind Avatar answered Oct 17 '22 10:10

iammilind


The error is correct. An implicit conversion (in this case, via your operator) is only considered if the types are not reference-related. The inheritance relationship means they are, so the reference would bind directly without conversion, but that fails due to the private inheritance.

Unless you have a good reason for inheritance, you might make the A a member rather than a base class. In that case, a conversion operator returning a reference to that member would do what you want.

If you do need the inheritance, then a suitably named function might be nicer than requiring an explicit operator call.

like image 41
Mike Seymour Avatar answered Oct 17 '22 09:10

Mike Seymour