From my wrapper class Pointer<Base>
I'd like to only return pointers to const: Base const *
.
When casting Pointer<Base>
to Derived const *
I get a compile error:
error C2440: 'static_cast': 'Pointer' can not be converted to 'const Derived *'
(translated from german VS2012)
struct Base { };
struct Derived : public Base { };
template <typename T>
class Pointer {
public:
Pointer(T *t = nullptr) : p(t) { }
//operator T*() { return p; }
operator T const *() const { return p; }
template <typename U>
inline U staticCast() const { return static_cast<U>(d); }
private:
T *p;
};
int main(int argc, char *argv[]) {
Derived d;
Pointer<Base> p(&d);
Derived const *pd = static_cast<Derived const *>(p);
}
If I enable the conversion operator T*() { return p; }
it works.
Why doesn't static_cast
use the const conversion operator?
Or more specifically, since
Derived const *pd = static_cast<Derived const *>(static_cast<Base const *>(p));
works:
Why can static_cast
implicitly cast to Base *
, but not to Base const *
, even though the latter is sufficient for the cast target type?
If there is an implicit conversion sequence from expression to new_type, or if overload resolution for a direct initialization of an object or reference of type new_type from expression would find at least one viable function, then static_cast(expression) returns the imaginary variable Temp initialized as if by new_type Temp(expression);, which may involve implicit conversions, a call to the constructor of new_type or a call to a user-defined conversion operator.
[Emphasis by me]
Workaround
Since this seems like a VisualStudio bug, I will use a workaround instead by means of a templated member function staticCast()
(see sample code above), to be used like this:
Derived const *pd = p.staticCast<Derived const *>();
To allow only casts to U const *
, use SFINAE:
template <typename U>
struct is_pointer_to_const
{
static const bool value = std::is_pointer<U>::value
&& std::is_const<typename std::remove_pointer<U>::type >::value;
};
template <typename U>
inline U staticCast(typename std::enable_if<is_pointer_to_const<U>::value >::type* = 0) const
{ return static_cast<U>(d); }
template <typename U>
inline U staticCast(typename std::enable_if<!is_pointer_to_const<U>::value >::type* = 0) const
{ static_assert(false, "Type is not a pointer to const"); return U(); }