标准是否阻止在可变参数模板中使用足够小的文本值缩小文本转换范围

Does the standard prevent narrowing conversion of literal with small enough literal values within variadic templates

本文关键字:文本 转换 缩小 范围 是否 变参 参数 标准      更新时间:2023-10-16

下面是一个最小的例子:

#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};

那么这就可以了,因为编译器知道5127是可表示的。 不过你的情况不一样。 您在函数内部和函数内部进行初始化{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>();