Почему static_cast не использует оператор преобразования для указания на const? - PullRequest
0 голосов
/ 10 января 2019

Из моего класса-оболочки Pointer<Base> Я бы хотел вернуть только указатели на const: Base const *.
При приведении Pointer<Base> к Derived const * я получаю ошибку компиляции:

ошибка C2440: «static_cast»: «Указатель» не может быть преобразован в «const Derived *»

(перевод с немецкого 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);
}

Если включить преобразование operator T*() { return p; }, оно будет работать.

Почему static_cast не использует оператор преобразования констант?

Или, точнее, с

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

работает:

Почему static_cast может неявно приводиться к Base *, но не к Base const *, хотя последнего достаточно для целевого типа приведения?


Стандарт гласит :

Если существует неявная последовательность преобразования из выражения в new_type или если при разрешении перегрузки для прямой инициализации объекта или ссылки типа new_type из выражения найдется хотя бы одна жизнеспособная функция, то static_cast (expression) возвращает мнимую переменную Temp инициализируется как будто с помощью new_type Temp (выражение); это может включать неявные преобразования , вызов конструктора new_type или вызов пользовательского оператора преобразования .

[Акцент мной]


Обход

Поскольку это похоже на ошибку VisualStudio, вместо этого я буду использовать обходной путь с помощью шаблонной функции-члена staticCast() (см. Пример кода выше), которая будет использоваться следующим образом:

Derived const *pd = p.staticCast<Derived const *>();

Чтобы разрешить только приведение к U const *, используйте 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(); }

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Допускается только одно преобразование, поэтому вы можете преобразовать в Base, но впоследствии оно не может быть преобразовано в Derived.

Таким образом, вы должны использовать два последовательных приведения. В любом случае это безопаснее, потому что вы заявляете, что знаете, что конвертируете из Base в Derived. Вы никогда не должны иметь неявного преобразования из базового класса в производный класс.

0 голосов
/ 10 января 2019

Вам нужно обработать в два этапа, когда вы пытаетесь конвертировать Pointer<Base>* --- (1) ---> Base const* --- (2) ---> Derived const*, с помощью:

  1. Pointer<Base>::operator Base const*
  2. подавленным.

, например

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

Живая демоверсия .

...