static/dynamic/const/reinterpret_cast 可以在未计算的上下文中使用吗?

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

我试图提供用于检查的结构 A 是否(选择强制转换)- 可强制转换为 B。 所有四个强制类型转换都将具有完全相同的实现,期望它们的名称(本地宏定义是可能的,但不是必需的)。我写了很多 check-for operators 结构,例如:

#include <iostream>
#include <type_traits>
#include <string>

template<class, class, class, class = void>
struct is_valid_ternary_operator : std::false_type
{ };

template<class T, class S, class R>
struct is_valid_ternary_operator <T, S, R, 
                                  std::void_t<decltype(std::declval<T>() ?
                                                       std::declval<S>() :
                                                       std::declval<R>())>> : std::true_type
{ };

int main()
{
                                          //true? 1 : 0 //is ok
    std::cout << is_valid_ternary_operator<bool, int, int>::value << std::endl;
                                          //true? 1 : std::string("0") //would be error
    std::cout << is_valid_ternary_operator<bool, int, std::string>::value << std::endl;
                                          //true? std::string("1") : std::string("0") //ok
    std::cout << is_valid_ternary_operator<bool, std::string, std::string>::value << std::endl;
                                          //std::string("1")? 1 : 0 //error
    std::cout << is_valid_ternary_operator<std::string, int, int>::value << std::endl;
}

Live example

显示预期的输出。但现在考虑对演员表做同样的事情:

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::void_t<decltype(static_cast<std::declval<S>()>
                                                           (std::declval<T>()))>> : std::true_type
{ };

但它会产生错误:

main.cpp:12:84: error: template argument 1 is invalid
    (std::declval<T>()))>> : std::true_type
                        ^~
main.cpp:12:94: error: template argument 3 is invalid
    (std::declval<T>()))>> : std::true_type
                                  ^~~~~~~~~

Live

是否不允许在未计算的上下文中使用强制转换?

Is using casts in unevaluated context not allowed?

简短回答:是的,这是允许的。


无论如何,我会尝试将示例简化为最小的工作示例。
请注意 castsexpressions 并且它们应该与操作数未评估的运算符一起工作(sizeofnoexcept, decltype, typeid), 除非明确说明相反。

例如,sizeof 是一个未计算的上下文:

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

一个简单得多的例子,而且有效。
同样可以使用 decltype 显示,其操作数也未计算:

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

等等,我们可以用noexcept做类似的事情:

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

typeid:

#include <typeinfo>

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

is_static_castable<T, S> 听起来像是 C++ 标准库中已经存在的东西。作为 std::is_convertible.

关于您的问题:static_cast 并不总是意味着编译时间。例如,在整数和浮点类型的转换中,如果编译器无法在编译时执行转换,则编译器可能会选择发出转换代码。尽管。它很可能出现在未评估的上下文中。

C++ 中未计算的操作数非常有限: 上下文是:

  • decltype
  • sizeof
  • typeid
  • noexcept

引用C++标准草案,[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.


注意:std::is_convertible 并未涵盖 static_cast

的所有情况

因此,为了完整起见,这就是您想要对 is_static_cast_able 执行的操作,几乎涵盖了 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 { };

以下测试代码通过:

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, "");
}

看到了Live on Coliru