Conversion from Derived** to Base**
It's basically the same reason why a bowl of bananas is not a bowl of fruits. If a bowl of bananas were a bowl of fruits, you could put an apple into the bowl, and it would no longer be a bowl of bananas.
As long as you only inspect the bowl, the conversion is harmless. But as soon as you start modifying it, the conversion becomes unsafe. This is the key point to bear in mind. (This is the precise reason why the immutable Scala collections actually allow the conversion, but the mutable collections prohibit it.)
Same with your example. If there was a conversion from Derived**
to Base**
, you could put a pointer to an apple were the type system promised only a pointer to a banana could exist. Boom!
Converting from Derived* to Base*&
An object of type Child*
cannot be bound to a Parent*&
for exactly the same reason that a Child**
cannot be converted to a Parent**
. Allowing it would allow the programmer (intentionally or not) to break type safety without a cast.
class Animal {};
class DangerousShark : public Animal {};
class CuteKitten : public Animal {};
void f(Animal*& animalPtrRef, Animal* anotherAnimalPtr)
{
animalPtrRef = anotherAnimalPtr;
}
void g()
{
DangerousShark myPet;
CuteKitten* harmlessPetPtr;
f(harmlessPetPtr, &myPet); // Fortunately, an illegal function call.
}
Edit
I think that some of the confusion arises because of the loose use of the words 'convert' and 'conversion'.
References can't be rebound, unlike objects which can be reassigned, so in the context of references when we speak of conversion we can only be concerned about initializing a new reference.
References are always bound to an object, and from the OP's question it was clear that he is aiming to get a reference that is a direct bind to an existing object. This is only allowed if the object used to initialize the reference is reference-compatible with the type of the reference. Essentially, this is only if the types are the same, or the type of the object is derived from the type of the reference and the reference type is at least as cv-qualified as the initializing object. In particular, pointers to different types are not reference-compatible, regardless of the relationship of the pointed-to types.
In other cases, a reference can be initialized with something that can be converted to the reference type. In these cases, though, the reference must be const and not volatile and the conversion will create a temporary and the reference will be bound to this temporary and not the original object. As pointed out, this is not suitable for the requirements of OP's motivating example.
In summary, a Child
can be bound directly to a Parent&
but a Child*
cannot be directly bound to a Parent*&
. A Parent* const&
can be initialized with a Child*
, but the reference will actually bind to a temporary Parent*
object copy-initialized from the Child*
object.
Why would the conversion between derived* to base* fails with private inheritance?
$11.2/4 states-
A base class B of N is accessible at R, if
- an invented public member of B would be a public member of N, or
- R occurs in a member or friend of class N, and an invented public
member of B would be a private or
protected member of N, or- R occurs in a member or friend of a class P derived from N, and an
invented public member of B would be a
private or protected member of P, or- there exists a class S such that B is a base class of S accessible at R
and S is a base class of N accessible
at R."
Here 'B' is 'Base', 'N' is 'Derived' and 'R' is main.
Consider the 2nd bullet- 'R occurs in a member or friend of a class N,...'. This clause does not apply as 'R'(main) is neither a member nor friend of 'N'(Derived)
Consider the 3rd bullet- 'R occurs in a member or friend of a class P....'. This claus also does not apply for the same reasons as above
Consider the 4th bullet- Once again this clause does not apply
Thus we can conclude that 'Base' is not an accessible class of 'Derived'.
$11.2/5 states -
If a base class is
accessible, one can implicitly convert
a pointer to a derived class to a
pointer to that base class (4.10,
4.11). [ Note: it follows that members and friends of a class X can
implicitly convert an X* to a pointer
to a private or protected immediate
base class of X. —end note ]
Since Base
is not an accessible class of Derived
when accessed in main
, the Standard conversion from Derived class to Base class is ill-formed. Hence the error.
EDIT 2:
Study the error messages of some popular compilers and that should help you get a better understanding. Note how the word 'inaccessible' pops up so frequently and consistently across all the error messages
The references are from the draft standard N3000. I am yet to download the latest draft :)
GCC prog.cpp: In function ‘int
main()’: prog.cpp:27: error: ‘base’ is
an inaccessible base of ‘derived’Comeau Online "ComeauTest.c", line 26:
error: conversion to inaccessible base
class "base" is not
allowed
ptr = new derived;VS2010 error C2243: 'type cast' :
conversion from 'derived *' to 'base
*' exists, but is inaccessible
Converting Derived** to Base** and Derived* to Base*
nasty_function(d); // Ooops, now *d points to a Base. What would happen now?
No, it doesn't. It points to a Derived
. The function simply changed the Base
subobject in the existing Derived
object. Consider:
#include <cassert>
struct Base {
Base(int x) : x(x) {}
int x;
};
struct Derived : Base {
Derived(int x, int y) : Base(x), y(y) {}
int y;
};
int main(int argc, char **argv)
{
Derived d(1,2); // seriously, WTF is it with people and new?
// You don't need new to use pointers
// Stop it already
assert(d.x == 1);
assert(d.y == 2);
nasty_function(&d);
assert(d.x == 3);
assert(d.y == 2);
}
d
doesn't magically become a Base
, does it? It's still a Derived
, but the Base
part of it changed.
In pictures :)
This is what Base
and Derived
objects look like:
When we have two levels of indirection it doesn't work because the things being assigned are pointers:
Notice how neither of the Base
or Derived
objects in question are attempted to be changed: only the middle pointer is.
But, when you only have one level of indirection, the code modifies the object itself, in a way that the object allows (it can forbid it by making private, hiding, or deleting the assignment operator from a Base
):
Notice how no pointers are changed here. This is just like any other operation that changes part of an object, like d.y = 42;
.
Pointer-to-Member invalid conversion from ‘Derived X::*’ to ‘Base X::*’
Base X::*
means a pointer to a member of X having type Base.
It is not the same as
Base*
There are no conversion from
Base*
to
Base X::*
and consequently no conversion from
Derived*
to
Base X::*
Likewise there are no conversion between Base X::*
and Derived X::*
Example:
#include <iostream>
using namespace std;
class Base
{
};
class Derived : public Base
{
};
class X {
public:
Derived field1;
Base field2;
};
int main() {
Base X::* ptr1 = &X::field1; // Derived X::* to Base X::* OK ?
Derived X::* ptr2 = &X::field2; // Base X::* to Derived X::* OK ?
return 0;
}
This will result in
prog.cpp:20:28: error: invalid conversion from 'Derived X::*' to 'Base X::*' [-fpermissive]
Base X::* ptr1 = &X::field1;
^
prog.cpp:21:31: error: invalid conversion from 'Base X::*' to 'Derived X::*' [-fpermissive]
Derived X::* ptr2 = &X::field2;
So in order to compile, it needs to be:
int main() {
Derived X::* ptr1 = &X::field1;
Base X::* ptr2 = &X::field2;
return 0;
}
Below is an example of how pointer-to-member can be used:
#include <iostream>
#include <vector>
using namespace std;
class Base
{
public:
Base(int g1) : g(g1) {}
int g;
};
class Derived : public Base
{
public:
Derived(int d) : Base(d) {}
};
class X {
public:
X(int f1, int f2) : field1(f1), field2(f2) {}
Derived field1;
Derived field2;
};
void foo(vector<X>& vx, Derived X::*d)
{
cout << "foo" << endl;
for (auto& x : vx)
{
cout << (x.*d).g << endl;
}
}
int main() {
vector<X> vx {{5, 10}, {50, 100}};
foo(vx, &X::field1); // Print field1.g of all elements in vector vx
foo(vx, &X::field2); // Print field2.g of all elements in vector vx
return 0;
}
This will output:
foo
5
50
foo
10
100
Conversion from Derived** to Base*const*
You're confusing two cases:
- The addition of
const
- Upcasts
While formally (in computer science theory) both of these deal with subclassing, the reality is that the C++ rules for these are different, because the representation of const T
and T
are guaranteed to be the same, while the representations of Base*
and Derived*
often differ by an offset (but may be radically different when virtual inheritance is involved).
In 3.9.3, the Standard declares that
The cv-qualified or cv-unqualified versions of a type are distinct types; however, they shall have the same representation and alignment requirements
Given:
struct Base {};
struct Derived : Base {};
Derived* pd = nullptr;
Base* pb = pd;
const
can indeed by added in the way you suggest.
Base const* const* const cpcpcb = &pb;
Base* const* pcpb = &pb; // legal, pointer can't be changed
Base const* * ppcb = &pb; // illegal, one could try to rebind the pointer
// to a truly const object, then
// use pb to mutate the const object
But there is no is-a relationship between Derived*
and Base*
. A conversion exists, but the Derived*
variable does not necessarily contain the address of a Base
object (the Base
subobject within the Derived
object may have a different address). And therefore both the line you're complaining about, and the line your question assumed was valid, are illegal:
Base const* const* const cpcpcd = &pd; // error, there's no address of a Base
// to be found in pd
Base* const* pcpd = &pd; // error: again, there's no address of a Base
// stored in pd
Formally, the Standard describes this in 4.10:
A prvalue of type "pointer to cv
D
”, whereD
is a class type, can be converted to a prvalue of type "pointer to cvB
", whereB
is a base class ofD
. IfB
is an inaccessible or ambiguous base class ofD
, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.
The result of the conversion is a prvalue, it doesn't have an address, and you can't create a pointer to it.
Cast derived class to base
This is a standard derived-to-base pointer conversion. The rules are that a pointer to D
with some const
/volatile
qualifications can be converted to a pointer to B
with the same qualifiers if B
is a base class of D
.
The standard conversions are implicit conversions with built-in meanings and are separate concepts to things like static_cast
or C-style casts.
Generally it's best to rely on implicit conversions when you can. Explicit conversions add more code noise and may hide some maintenance mistakes.
Automatic conversion from derived class to base class' member variable's type
When compiler sees d == B{2}
, it first creates a list of operator==
overloads that it's able to find, and then performs overload resolution on them.
As the link explains, the overload list contains:
- Member
operator==
s of the first operand, if any. - Non-member
operators==
s found by unqualified lookup, if any. - Built-in
operator==
s, if your operands can be converted to built-in types.
There's no mention of examining conversion operators of the first operand, so your operator==
doesn't get found.
The solution is to make the operator==
non-member (possibly define it as a friend
). This works:
friend bool operator==(const B &a, const B &b) {return a.x == b.x;}
Starting from C++20, the rules of comparison operators got relaxed: the compiler will look for member operator==
in the second operand as well, and will happily call non-member operator==
with arguments in a wrong order.
So a == b
and b == a
are now equivalent to a degree, except that:
- An
operator==
called in this new manner must returnbool
. - If given choice, the compiler will prefer the old ways of calling
operator==
over calling the memberoperator==
of the rhs, which is in turn preferred over calling a non-memberoperator==
with a wrong argument order.
C2440 when assigning `Derived**` to `Base**`
The answer lies in what a cast to base pointer actually does in C++. Consider the following simple example:
struct A { std::uint32_t a; };
struct B { std::uint32_t b; };
struct D : A, B { std::uint32_t d; };
Here D
has two bases, A
and B
. But only one of them can live at the beginning of D
(lets say it is A
), so when you convert the D
to the other one (B
), the value of the pointer needs to change, to point at some point in the middle of D
.
To visualize this, consider how an object of type D
looks like in your memory:
0 1 2 3 4 5 6 7 8 9 A B
[ A::a ][ B::b ][ D::d ]
^ a D* would point here
^ a A* would point here
^ a B* must point here
Whilst D
expects all three of these integers, when thinking of this object as an A
, we only expect the first one, but the pointer is (numerically) the same, we only expect a shorter object. However, when thinking of it as an B
, we need to point at the one integer in the middle.
When you do that to one pointer, the compiler will take care of this for you by changing the value of the pointer appropriately. However, when you do that to an array of pointers, the compiler would need to emit a loop over all elements of that and correct every single pointer - when in fact it cannot even know where the array ends (since the compiler only sees a pointer to its beginning)!
Therefore C++ disallows this kind of conversion.
Related Topics
How to Not Wait for a System() Command to Finish? (In C)
Std::Async Won't Spawn a New Thread When Return Value Is Not Stored
Inserting Elements in Multidimensional Vector
How to Allocate a Specific Memory Address Using Pointers in C++
Reading JSON File with C++ and Boost
Alpha Rendering Difference Between Opengl and Webgl
Compiling an Application for Use in Highly Radioactive Environments
What Is the Purpose of the "Final" Keyword in C++11 for Functions
String::Size_Type Instead of Int
Is It Ok to Specialize Std::Numeric_Limits<T> for User-Defined Number-Like Classes
Efficient Multiply/Divide of Two 128-Bit Integers on X86 (No 64-Bit)
What Is the Class Keyword Before a Function Argument
Does Boost::Bind() Copy Parameters by Reference or by Value
Are the "Usual Arithmetic Conversions" and the "Integer Promotions" the Same Thing