为什么在 gcc 的 is_nothrow_constructible 实现中需要 static_cast?

Why is static_cast needed in the gcc's implementation of is_nothrow_constructible?

摘自 type_traits 的 GCC 实现,为什么这里需要 static_cast

template <typename _Tp, typename... _Args>
struct __is_nt_constructible_impl
    : public integral_constant<bool, noexcept(_Tp(declval<_Args>()...))> {};

template <typename _Tp, typename _Arg>
struct __is_nt_constructible_impl<_Tp, _Arg>
    : public integral_constant<bool,
                               // Why is `static_cast` needed here?
                               noexcept(static_cast<_Tp>(declval<_Arg>()))> {};

如果发明的变量声明

,则类型不可从参数列表中构造
T t(declval<Args>()...);

将是 well-formed and is known not to throw exceptions. In the plural argument case this is equivalent (modulo noexcept destructibility, see LWG 2116) to the well-formedness and nothrow of the type conversion expression

T(declval<Args>()...)

然而,在单参数情况下,表达式 T(declval<Args>()) 被视为 cast-expression, which can invoke const_cast and reinterpret_caststatic_cast 的显式使用恢复了声明形式的等价性。

作为 concrete example,考虑以下类型:

struct D;
struct B { operator D&&() const; };
struct D : B {};

这里从B constD&&static_cast必须使用转换运算符,但是强制转换表达式可以绕过转换运算符,noexcept也是如此。所以省略 static_cast 会给 is_nothrow_constructible<D&&, B const>.

错误的结果