Background information: This was detected on Visual Studio 2008, and confirmed again on Visual Studio 2013. G++ screamed at the code, while Visual accepted the private inheritance breach silently.
So, on Visual C++, we have the following code:
class Base {};
class Derived : Base {}; // inherits privately. Adding explicitly the
// keyword private changes nothing
int main()
{
std::auto_ptr<Base>(new Derived) ; // compiles, which is NOT EXPECTED
std::auto_ptr<Base> p(new Derived) ; // Does not compile, which is expected
}
Why would the first (temporary) auto_ptr compile? I went inside it in debug, it did exactly what is was supposed to do with a public inheritance (call the right constructor, etc.)
Wondering if perhaps the issue was with the auto_ptr implementation (we never know...), I reduced the issue on this standalone code:
class Base {};
class Derived : Base {};
template <typename T>
class Ptr
{
T * m_p;
public :
Ptr(T * p_p)
: m_p(p_p)
{
}
} ;
int main()
{
Ptr<Base>(new Derived) ; // compiles, which is NOT EXPECTED
Ptr<Base> p(new Derived) ; // Does not compile, which is expected
}
Again, I expected the code to NOT compile, as Derived inherits privately from Base.
But when we create a temporary, it works.
And we can't blame it on std::auto_ptr.
Is there something in the standard (either 98 or 11 or 14) I missed, or is this a bug?
The Derived*
-to-Base*
conversion, even if the inheritance is private, is permitted in C-style and functional casts. And no, it doesn't mean reinterpret_cast
in that case.
This isn't allowed by the standard, but it very nearly looks as if it is allowed, so it's a subtle bug.
5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
1 [...] If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). [...]
5.4 Explicit type conversion (cast notation) [expr.cast]
4 The conversions performed by
- a
const_cast
(5.2.11),
- a
static_cast
(5.2.9),
- a
static_cast
followed by a const_cast
,
- a
reinterpret_cast
(5.2.10), or
- a
reinterpret_cast
followed by a const_cast
,
can be performed using the cast notation of explicit type conversion. The same semantic restrictions and behaviors apply, with the exception that in performing a
static_cast
in the following situations the conversion is valid even if the base class is inaccessible:
- a pointer to an object of derived class type or an lvalue or rvalue of derived class type may be explicitly
converted to a pointer or reference to an unambiguous base class type, respectively;
- [...]
In the situation you've got, the compiler interprets it as a static_cast
from Derived*
to auto_ptr<Base>
, and in that static_cast
, a pointer to an object of derived class type is converted to a pointer of an unambiguous base class type. So it looks like the standard allows it.
However, the conversion from Derived*
to Base*
is implicit, it merely happens to be performed as part of an explicit different conversion. So in the end, no, the standard really doesn't allow it.
You may want to report this as a bug. From Csq's comment, we learn that there is a related report, in which an explicit static_cast
also allows this conversion, but it's not exactly the same thing. In that case, the conversion from Derived*
to Base*
is explicit, but it is implicit here, and Visual C++ does usually reject that in implicit conversions.
Note that in functional casts that use multiple expressions, this misinterpretation isn't possible: the compiler correctly rejects the following:
class Base { };
class Derived : Base { };
template <typename T>
class Ptr {
public:
Ptr(T *a, T *b) { }
};
int main() {
Ptr<Base>(new Derived, new Derived);
// error C2243: 'type cast' : conversion from 'Derived *' to 'Base *' exists, but is inaccessible
}