首页 > 解决方案 > 该标准是否会阻止在可变参数模板中使用足够小的文字值缩小文字转换的范围

问题描述

这是最小的示例:

#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


奖金问题:

标签: c++variadic-templatesconstexprlist-initialization

解决方案


由于在这种情况下,编译器拥有静态验证act(5, 5)对提供的值的调用不会溢出所需的一切,因此在此代码上失败是否符合标准的行为?

是的。只有当编译器可以保证值可以用更窄的类型表示时,才允许编译器进行窄化转换。如果你有

std::array<unsigned char, 2> foo = {5, 127};

那么这没关系,因为编译器知道5并且127是可表示的。你的情况虽然不一样。您在函数内部进行初始化,并且函数内部{aArgs...}没有相同的保证,因为它不是常量表达式(传递给函数的变量都不是常量表达式)。因此,编译器无法在所有情况下都证明它是有效的,因此它会发出警告/错误。

由于没有文字后缀来获取 unsigned char 文字,如何解决此错误 || 修复这个非标准代码?

您可以让自己的用户定义 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中找到

缩小转换是一种隐式转换 [...}

  • 从整数类型或无作用域枚举类型到浮点类型,除非源是常量表达式并且转换后的实际值将适合目标类型,并且在转换回原始类型时将产生原始值 [. ..]

强调我的

如您所见,它要求初始化程序是一个常量表达式。由于函数参数从来都不是常量表达式,因此编译器进行多少静态分析以及它有多少证明都无关紧要。它不是一个常量表达式,因此无法完成。


推荐阅读