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

11

从 GCC 实现的 type_traits 中获取,为什么这里需要使用 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>()))> {};

那种不一致确实很奇怪。 - Lightness Races in Orbit
4
你应该在相关的libstdc++邮件列表上提出这样的问题。 - Lightness Races in Orbit
1个回答

12
如果从参数列表中发明一个变量声明,那么该类型是nothrow可构建的。
T t(declval<Args>()...);

在复数参数的情况下,类型转换表达式将是格式良好已知不会抛出异常。除了不可抛性析构(见LWG 2116)外,这与单一参数的情况等效。
T(declval<Args>()...)

然而,在单参数情况下,表达式T(declval<Args>())被视为cast-expression,可以调用const_castreinterpret_cast;显式使用static_cast可恢复到声明形式的等效性。
作为一个具体例子,考虑以下类型:
struct D;
struct B { operator D&&() const; };
struct D : B {};

这里需要使用转换运算符来进行从B constD&&static_cast,但是一个强制类型转换表达式可以绕过转换运算符,因此是不抛出异常的。因此,省略static_cast会给is_nothrow_constructible<D&&, B const>带来错误的结果。


所以需要使用 static_cast,这样表达式就总是被视为“直接初始化”,而不是“强制转换表达式”? - João Pires
1
@JoãoPires 是的,没错。虽然使用 noexcept 运算符无法测试声明的 noexcept,因此仍不完全符合标准要求,但已经非常接近了。 - ecatmur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接