下面是一个最小的例子:
#include <array>
template <class... T>
constexpr std::array<unsigned char, sizeof...(T)> act(T... aArgs)
{
return std::array<unsigned char, sizeof...(T)>{aArgs...};
}
int main()
{
act(5, 5);
}
编辑GCC 和 Clang 可以毫无怨言地编译该片段的地方不,他们不能。
最新的 MSVC 失败,并显示:
<source>(6): error C2397: conversion from 'int' to '_Ty' requires a narrowing conversion
with
[
_Ty=unsigned char
]
请参阅:https://godbolt.org/z/1PmeLk
- 由于在这种情况下,编译器拥有静态验证对
act(5, 5)
的调用不会溢出所提供值所需的一切,因此此代码失败是否是符合标准的行为?
奖金问题:
- 由于没有文字后缀来获取无符号字符文字,因此如何解决此错误
||
修复此非标准代码?
,编译器拥有静态验证对
act(5, 5)
的调用不会因提供的值而溢出所需的一切,因此在此代码上失败是否是一种符合标准的行为?
是的。 仅当编译器可以保证该值以较窄的类型表示时,才允许编译器执行缩小转换。 如果你有
std::array<unsigned char, 2> foo = {5, 127};
那么这就可以了,因为编译器知道5
和127
是可表示的。 不过你的情况不一样。 您在函数内部和函数内部进行初始化{aArgs...}
没有相同的保证,因为它不是常量表达式(传递给函数的变量都不是常量表达式(。 因此,编译器无法在所有情况下证明它是有效的,因此它会发出警告/错误。
由于没有文字后缀来获取无符号字符文字,如何解决此错误 || 修复此非标准代码?
您可以让自己的用户定义 litteral 以制作无符号字符,例如
inline constexpr unsigned char operator ""_uc( unsigned long long arg ) noexcept
{
return static_cast< unsigned char >( arg );
}
//...
act(5_uc, 5_uc);
或者你可以像
act((unsigned char)5, (unsigned char)5);
涵盖此案例的实际措辞可以在 [dcl.init]/7 中找到
。缩小转换是隐式转换 [...}
- 从整数类型或无作用域枚举类型到浮点类型,除非源是常量表达式,并且转换后的实际值将适合目标类型,并在转换回原始类型时生成原始值 [...]
强调我的
如您所见,它要求 initizlizer 是一个常量表达式。 由于函数参数从来都不是常量表达式,因此编译器执行多少静态分析以及它有多少证明并不重要。 它不是一个常量表达式,因此无法完成。
奖金问题:
由于没有文字后缀来获取无符号字符文字,如何解决此错误 || 修复此非标准代码?
只是为了好玩,我向你推荐一种方法(不是很实用(将你的文字类型值作为函数的参数传递(在某种意义上(,避免缩小问题:你可以通过一个std::integral_constant
接收 args 值
template <typename ... T, T ... args>
constexpr std::array<unsigned char, sizeof...(T)>
act (std::integral_constant<T, args>...)
{
return std::array<unsigned char, sizeof...(T)>{args...};
}
并按如下方式调用
act(std::integral_constant<int, 5>{},
std::integral_constant<long long, 5ll>{});
这样,args
值是模板值,因此编译器可以保证从缩小的角度来看,特定函数实例化是安全的(显然,使用足够的值调用它(。
如果可以使用 C++17,则只需将 args 作为模板值传递auto
即可获得相同的结果(以更简单、更实用的方式(。
template <auto ... args>
constexpr auto act ()
{
return std::array<unsigned char, sizeof...(args)>{args...};
}
// ...
act<5, 5ll>();