Неправильно ли используется static_cast?

У меня смешанные чувства по поводу static_cast, так как это самый безопасный из доступных преобразований C ++, но он позволяет одновременно и безопасные, и небезопасные преобразования, поэтому вы должны знать контекст, чтобы сказать, действительно ли это безопасно или может привести к UB (например, когда приведение к подклассу).

Так почему же нет более безопасного явного приведения? Вот пример, где это может быть полезно. В COM они должны возвращать указатель интерфейса как void** ppv, поэтому "должны" явно приводить

*ppv = (IInterface*) this;

который затем было предложено заменить более безопасным приведением C ++

*ppv = static_cast<IInterface*>(this);

Но есть ли смысл делать здесь даже static_cast? this относится к классу, производному от IInterface, поэтому можно просто написать

IInterface* p = this; // implicit conversion to base, safe for sure
*ppv = p;

или используйте помощника, например

template<class T, class U>
T implicit_cast(U p) { return p; }

*ppv = implicit_cast<IInterface*>(this);

Итак, правда ли, что static_cast иногда неправильно используется и может (должен?) Быть заменен этим implicit_cast в некоторых случаях, или я что-то упускаю?

РЕДАКТИРОВАТЬ: я знаю, что a Приведение требуется в COM, но не обязательно static_cast, достаточно неявного приведения.


person Roman L    schedule 10.02.2011    source источник
comment
Примечание. См. stackoverflow.com/a/869597 для правильной реализации implicit_cast (и хорошего объяснения).   -  person gx_    schedule 21.10.2013


Ответы (1)


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

Похоже, что использование вашего implicit_cast, вероятно, будет более безопасным и позволит вам явно выбрать, к какому базовому классу вы хотите неявно привести (что, по-видимому, требуется для COM).

Я провел быстрый тест с g ++, и implicit_cast действительно возвращает разные адреса для разных базовых классов, как и ожидалось.

Однако обратите внимание, что в отношении вашего самого первого предложения я бы сказал, что dynamic_cast на самом деле безопаснее, чем static_cast, поскольку он вернет null или throw, если приведение не может быть завершено. Напротив, static_cast вернет действительный указатель и позволит вам продолжать, пока ваша программа не взорвется когда-нибудь в будущем, не связанная с исходным плохим приведением.

Программа испытаний:

#include <iostream>

class B1
{
public:
    virtual ~B1() {}
};

class B2
{
public:
    virtual ~B2() {}
};

class Foo : public B1, public B2
{
};

template<class T, class U>
T implicit_cast(U p) { return p; }

int main()
{
    Foo* f = new Foo;
    void **ppv = new void*;

    *ppv = implicit_cast<B1*>(f);
    std::cout << *ppv << std::endl;;
    *ppv = implicit_cast<B2*>(f);
    std::cout << *ppv << std::endl;;

    return 0;
}
person Mark B    schedule 10.02.2011
comment
Действительно, dynamic_cast было бы безопаснее, чем static_cast, но я не упомянул об этом, потому что никогда не видел, чтобы он действительно использовался (я слышал о снижении производительности, но не знаю, насколько он велик). - person Roman L; 10.02.2011
comment
@ 7vies: В Visual C ++ это около 2 тысяч циклов процессора, и компилятор не поймает вас, если вы приведете свой COM-объект к неправильному типу - вы получите только нулевой указатель во время выполнения. - person sharptooth; 11.02.2011
comment
@sharptooth: Да, dynamic_cast определенно не лучший вариант в контексте COM. Я даже не уверен, в какой ситуации это вообще может быть хорошим вариантом - в большинстве случаев его можно заменить полиморфизмом времени выполнения или времени компиляции, который был бы безопаснее и быстрее одновременно. - person Roman L; 11.02.2011