Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does static_cast not use the conversion operator to pointer to const?

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?


The standard says:

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(); }
like image 855
Martin Hennings Avatar asked Jan 10 '19 13:01

Martin Hennings


People also ask

What does static_cast do in C?

The static_cast operator converts a null pointer value to the null pointer value of the destination type. Any expression can be explicitly converted to type void by the static_cast operator.

What static_cast is actually doing?

The static_cast operator converts variable j to type float . This allows the compiler to generate a division with an answer of type float . All static_cast operators resolve at compile time and do not remove any const or volatile modifiers.

What happens when static_cast fails?

As we learnt in the generic types example, static_cast<> will fail if you try to cast an object to another unrelated class, while reinterpret_cast<> will always succeed by "cheating" the compiler to believe that the object is really that unrelated class.

What is difference between static_cast and dynamic_cast?

static_cast − This is used for the normal/ordinary type conversion. This is also the cast responsible for implicit type coersion and can also be called explicitly. You should use it in cases like converting float to int, char to int, etc. dynamic_cast −This cast is used for handling polymorphism.


2 Answers

There is only one conversion allowed, so you can convert to Base, but it cannot be converted afterwards to Derived.

So you have to use two consecutive casts. It's safer anyway because you state that you know that you are converting from a Base to a Derived. You should never have an implicit conversion from a base class to a derived class.

like image 156
Matthieu Brucher Avatar answered Sep 17 '22 04:09

Matthieu Brucher


You need to process in two steps as you're trying to convert Pointer<Base>* ---(1)---> Base const* ---(2)---> Derived const*, with:

  1. Pointer<Base>::operator Base const*
  2. downcast.

e.g.

Base const* pb = static_cast<Base const *>(p);
Derived const *pd = static_cast<Derived const*>(pb);

Live demo.

like image 32
YSC Avatar answered Sep 19 '22 04:09

YSC