Can static/dynamic/const/reinterpret_cast be used in unevaluated context?

我只是一个虾纸丫 提交于 2019-12-06 00:05:58

Is using casts in unevaluated context not allowed?

Short answer: yes, it is allowed.


Anyway, I would try to reduce the example to a minimal, working one.
Note that casts are expressions and they are expected to work with operators the operands of which are unevaluated (sizeof, noexcept, decltype, typeid), unless explicitly stated the opposite.

As an example, sizeof is an unevaluated context:

int main() {
    unsigned int i;
    sizeof(static_cast<int>(i));
}

Far easier an example, and it works.
The same can be shown using decltype, the operands of which are unevaluated as well:

int main() {
    unsigned int i;
    decltype(static_cast<int>(i)) j = i;
}

And so on, we can do something similar both with noexcept:

int main() {
    unsigned int i;
    bool b = noexcept(static_cast<int>(i));
}

And with typeid:

#include <typeinfo>

int main() {
    unsigned int i;
    auto b = typeid(static_cast<int>(i)).name();
}

is_static_castable<T, S> sounds like something already existing in the C++ standard library. as std::is_convertible.

And to your question: static_cast doesn't always mean compile time. For example, in the conversions of integer and floating point types, the compiler may chose to emit code for the conversion if it cannot do so at compile-time. Though. it can very much appear in an unevaluated context.

Unevaluated operands in C++ are very limited: The contexts are:

  • decltype
  • sizeof
  • typeid
  • noexcept

To quote C++ standard draft, [expr/8]

In some contexts, unevaluated operands appear ([expr.typeid], [expr.sizeof], [expr.unary.noexcept], [dcl.type.simple]). An unevaluated operand is not evaluated. An unevaluated operand is considered a full-expression.


NOTE: std::is_convertible doesn't cover all cases of static_cast

Therefore, for completeness, this is what you want to do with is_static_cast_able, covering virtually all cases of static_cast:

template<class T, class S, class = void>
struct is_static_cast_able : std::false_type
{ };

template<class T, class S>
struct is_static_cast_able<T, S,
        std::enable_if_t<
            std::is_convertible<T, S>::value ||
            std::is_base_of<std::decay_t<T>, std::decay_t<S>>::value ||
            std::is_base_of<std::decay_t<S>, std::decay_t<T>>::value ||
            std::is_base_of<std::remove_pointer_t<T>, std::remove_pointer_t<S>>::value ||
            std::is_base_of<std::remove_pointer_t<S>, std::remove_pointer_t<T>>::value ||
            (std::is_same<T, void*>::value && std::is_pointer<S>::value) ||
            (std::is_same<S, void*>::value && std::is_pointer<T>::value)
        >
    >
: std::true_type { };

The following test code passes:

struct A {};
struct B : public A {};

int main()
{
    static_assert(is_static_cast_able<int, char>::value, "");
    static_assert(is_static_cast_able<double, int>::value, "");
    static_assert(is_static_cast_able<int, double>::value, "");
    static_assert(is_static_cast_able<void*, double*>::value, "");
    static_assert(is_static_cast_able<double*, void*>::value, "");
    static_assert(is_static_cast_able<B, A>::value, "");
    static_assert(is_static_cast_able<A, B>::value, "");
    static_assert(is_static_cast_able<B&, A&>::value, "");
    static_assert(is_static_cast_able<B*, A*>::value, "");
    static_assert(is_static_cast_able<A&, B&>::value, "");
    static_assert(is_static_cast_able<A*, B*>::value, "");
}

See it Live on Coliru

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!