C++ C++;如何在需要无符号的情况下阻止作为参数传递的负数

C++ C++;如何在需要无符号的情况下阻止作为参数传递的负数,c++,constructor,unsigned,C++,Constructor,Unsigned,我已经看到了线程,但是情况完全不同,我无法将该解决方案应用于我的问题 我有以下构造函数: Fan::Fan(Id id, std::string name, Age age); 其中,Id和Age是typedef的无符号int和无符号short。 这是给我的,所以我知道我必须使用它们,因为任务测试人员很可能会尝试使用大于int的数字(在unsigned int/short范围内) 显然,粉丝的id和age不能是负数。 在示例代码(要编译的代码)中,创建了一个风扇,其编号为id和age。我不能在

我已经看到了线程,但是情况完全不同,我无法将该解决方案应用于我的问题

我有以下构造函数:

Fan::Fan(Id id, std::string name, Age age);
其中,
Id
Age
typedef
的无符号int和无符号short。 这是给我的,所以我知道我必须使用它们,因为任务测试人员很可能会尝试使用大于int的数字(在unsigned int/short范围内)

显然,粉丝的
id
age
不能是负数。 在示例代码(要编译的代码)中,创建了一个风扇,其编号为
id
age
。我不能在那个里使用字符串并检查减号

当我在单元测试中输入负
age
/
id
时,我认为它不会编译。然而,事实证明它确实编译了,并且它给出了随机值(很可能是由于溢出原因)

因此,总结一下-可以使用
年龄
id
的负值调用构造函数,但在构造函数中,它们的值会被“重击”,并出现随机数,从而导致意外行为

例如,这:

Fan* fan = new Fan(-1, "Name", 9);
编译,并且在运行时风扇的
id
获取一些不相关的值。所以我必须能够检测构造函数中的负数。 如何“阻止”构造函数中的负数


谢谢你的时间!我希望我的问题是清楚的。

通过声明构造函数只接受无符号值,您基本上已经“阻止”了负数可以作为参数传递

您仍然可以编写传递给函数的
-1
,但函数随后看到的是该负数的无符号解释


例如,在32位windows应用程序中,传递
-1
将导致
Id
0xffffffff
4294967295
。现在您必须决定这是否是应用程序的有效输入,但可能是,因为它仍然是一个正数。另一方面,年龄
-1
可能是无效输入,因为它不太可能是4294967295岁。正如您所注意到的,您无法检查
-1
的无符号值。您必须检查
age
是否大于例如200,因为-1作为无符号参数给出时实际上是一个大数字。

我创建了一个小函数模板,用于检查输入是否在输出数据类型的范围内,否则会引发异常。它要求输入和输出数据类型是整数,并且输入数据类型的大小大于输出数据类型

template<typename DestType, typename SrcType>
DestType range_check_and_convert(SrcType const& src)
{
    static_assert(sizeof(SrcType) > sizeof(DestType), 
        "SrcType must be larger than DestType");
    static_assert(std::is_integral<SrcType>::value &&
                  std::is_integral<DestType>::value, "integral types only");

    if(src > static_cast<SrcType>(std::numeric_limits<DestType>::max())) {
        throw std::out_of_range("input too big");
    } else if(src < static_cast<SrcType>(std::numeric_limits<DestType>::min())) {
        throw std::out_of_range("input too small");
    }

    return static_cast<DestType>(src);
}
模板
DestType范围检查和转换(SrcType const&src)
{
静态断言(sizeof(SrcType)>sizeof(DestType),
“SrcType必须大于DestType”);
静态_断言(std::is_integral::value)&&
std::is_integral::value,“仅限整数类型”);
如果(src>static\u cast(std::numeric\u limits::max()){
抛出标准::超出范围(“输入太大”);
}否则如果(src

您希望这种行为是什么?您可以首先接受有符号整数,然后显式检查>=0。然后添加一个assert()终止程序,或者在检查失败时抛出一个异常如果age/id小于0,您可以检查输入,直到用户为类似于您的代码输入有效的input.clang时抛出错误,但GCC不会。MSVC2013也会为相同的代码生成警告C4245。如果您无法让编译器生成警告,您唯一的选择是让构造函数执行
long
long
(假设其中一个大于
int
)对于这些参数,在检查值是否大于0后,分别将其强制转换为
Id
Age
。@Zephyer在这种情况下,您也会抛出一个异常。静态强制转换是另一个绝妙的主意。如果我尝试将long long静态强制转换为unsigned int,会发生什么情况?@Zephyer,只要源值在转换的目标类型的范围定义良好。当从有符号转换为无符号时,如果源超出范围,则会得到模2^N环绕行为(其中N是目标类型中的位数)。但是,当使用超出签名类型范围的值从unsigned强制转换为signed时,行为是由实现定义的。请解释一下为什么通过const引用传递
src
参数?我想到的唯一解释是避免在
SrcType
不是内置的t例如,如果用户使用了一些任意精度的算法,比如
gmp
@MartinDrozdik,我这样做或多或少是出于习惯。正如您所指出的,如果您处理的是内置类型,那么就没有理由这么做。