C ++ vector :: size_type:signed vs unsigned; int与long

时间:2017-05-26 16:13:39

标签: c++ vector long-integer multiplatform

我一直在通过在不同平台上编译来对我的应用程序进行一些测试,而从64位系统转移到32位系统会暴露出许多问题。

我大量使用矢量,字符串等,因此需要对它们进行计数。但是,我的函数也使用32位无符号数,因为在很多情况下我需要显式地使用正整数。

我遇到的问题似乎很简单,例如std::minstd::max,这可能更具系统性。请考虑以下代码:

uint32_t getmax()
{
    return _vecContainer.size();
}

看起来很简单:我知道一个向量不能有负数的元素,所以返回一个无符号整数是完全合理的。

void setRowCol(const uint32_t &r_row; const uint32_t &r_col)
{
    myContainer_t mc;
    mc.row = r_row;
    mc.col = r_col;
    _vecContainer.push_back(mc);
}

再次,简单。

问题:

uint32_t foo(const uint32_t &r_row)
{
    return std::min(r_row, _vecContainer.size());
}

这给了我错误,例如:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/algorithm:2589:1: note: candidate template ignored: deduced conflicting types for parameter '_Tp' ('unsigned long' vs. 'unsigned int')
min(const _Tp& __a, const _Tp& __b)

我做了很多挖掘,在一个平台上,vector :: size_type是一个8字节的数字。但是,根据设计,我使用无符号的4字节数字。这可能会导致事情变得古怪,因为您无法隐式地将8字节数字转换为4字节数字。

解决方案是做老式的weay:

#define MIN_M(a,b) a < b ? a : b
return MIN_M(r_row, _vecContainer.size());

花花公子。但系统性问题仍然存在:在规划多平台支持时,您如何处理这样的实例?我可以使用size_t作为我的标准大小,但这增加了其他复杂性(例如从支持64位数的一个平台移动到另一个支持32位数字的平台)。更大的问题是size_t是无符号的,所以我无法更新我的签名:

size_t foo(const size_t &r_row)
// bad, this allows -1 to be passed, which I don't want

有什么建议吗?

编辑:我曾在某处读过size_t已签名,我已经纠正过了。到目前为止看起来这是我自己设计的限制(例如32位数与使用std :: vector :: size_type和/或size_t)。

2 个答案:

答案 0 :(得分:6)

解决这个问题的一种方法是使用

<DataGrid AutoGenerateColumns="True"
          ItemsSource="{Binding Path=Test.DefaultView}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="#"/>
    </DataGrid.Columns>
</DataGrid>

作为函数参数/返回的基础类型,如果使用C ++ 14则返回std::vector<Type>::size_type

答案 1 :(得分:4)

以一组花絮的形式回答:

  1. 您可以在使用std::min<T>等函数模板时显式指定类型,而不是依赖编译器来推断类型。例如:std::min<std::uint32_t>(4, my_vec.size());

  2. 启用与已签名与未签名比较和隐式缩小转换相关的所有编译器警告。尽可能使用大括号初始化,因为它会将缩小的转换视为错误。

  3. 如果您明确要使用像std::uint32_t这样的32位值,我会尝试找到显式转换的最小位数(即static_cast)&#34; sizes& #34;较小的类型。您不希望在任何地方使用转换,但如果您在内部使用库容器大小并且希望API使用std::uint32_t,则在API边界显式转换,以便您的类的用户永远不会担心自己做转换。如果你可以将转换保留在几个地方,那么添加运行时检查(即断言)变得切实可行,因为大小实际上并没有超出较小类型的范围。

  4. 如果您不关心确切的尺寸,请使用std::size_t,这对于所有标准容器几乎肯定与std::XXX::size_type相同。理论上它们可能是不同的,但它在实践中并没有发生。在大多数情况下,std::size_t不如std::vector::size_type那么冗长,因此它是一个很好的妥协。

  5. 许多人(包括C ++标准委员会中的许多人)都会告诉您,即使是大小和索引,也要避免使用无符号值。我理解并尊重他们的论点,但我发现它们没有足够的说服力来证明与标准库接口的额外摩擦。无论它是std::size_t未签名的历史工件,事实是标准库广泛使用无符号大小。如果您使用其他内容,您的代码最终会出现隐式转换,所有这些都是潜在的错误。更糟糕的是,那些隐式转换会使编译器警告变得不切实际,因此所有这些潜在错误仍然相对不可见。 (即使你知道你的大小永远不会超过较小的类型,被迫转换编译器警告签名和缩小意味着你可能会错过完全不相关的代码部分中的错误。)匹配你的API类型#39 ;尽可能多地使用,在必要时断言并明确转换,并打开所有警告。

  6. 请记住auto不是灵丹妙药。 for (auto i = 0; i < my_vec.size(); ++i) ...for (int i ...一样糟糕。但是如果你通常更喜欢算法和迭代器到原始循环,那么auto会让你走得很远。

  7. 除非你知道分母不是0,否则你必须永不分割。同样,对于无符号整数类型,除非你知道减数小于或等于原始值,否则你不能减去。如果你可以养成这个习惯,你就可以避免那些总是使用签名类型的人所关心的错误。