大多数签名/未签名警告的可接受修复?

Tob*_*obi 9 c++ unsigned stl coding-style

我自己确信,在一个项目中,我正在研究有符号整数是大多数情况下的最佳选择,即使其中包含的值永远不会是负数.(更简单的循环反转,更少的错误机会等,特别是对于只能保持0和20之间的值的整数,无论​​如何.)

出现问题的大多数地方是std :: vector的简单迭代,过去常常是一个数组,之后变为std :: vector.所以这些循环通常如下所示:

for (int i = 0; i < someVector.size(); ++i) { /* do stuff */ }
Run Code Online (Sandbox Code Playgroud)

由于此模式经常使用,因此编译器警告垃圾邮件的数量与签名和未签名类型之间的此比较往往隐藏更多有用的警告.请注意,我们肯定没有带有多个INT_MAX元素的向量,并注意到目前为止我们使用了两种方法来修复编译器警告:

for (unsigned i = 0; i < someVector.size(); ++i) { /*do stuff*/ }
Run Code Online (Sandbox Code Playgroud)

这通常有效但如果循环包含任何代码,如'if(i-1> = 0)......'等,可能会默默地中断.

for (int i = 0; i < static_cast<int>(someVector.size()); ++i) { /*do stuff*/ }
Run Code Online (Sandbox Code Playgroud)

这种变化没有任何副作用,但它确实使循环的可读性降低了很多.(而且它打字更多.)

所以我提出了以下想法:

template <typename T> struct vector : public std::vector<T>
{
    typedef std::vector<T> base;

    int size() const     { return base::size(); }
    int max_size() const { return base::max_size(); }
    int capacity() const { return base::capacity(); }

    vector()                  : base() {}
    vector(int n)             : base(n) {}
    vector(int n, const T& t) : base(n, t) {}
    vector(const base& other) : base(other) {}
};

template <typename Key, typename Data> struct map : public std::map<Key, Data>
{
    typedef std::map<Key, Data> base;
    typedef typename base::key_compare key_compare;

    int size() const     { return base::size(); }
    int max_size() const { return base::max_size(); }

    int erase(const Key& k) { return base::erase(k); }
    int count(const Key& k) { return base::count(k); }

    map() : base() {}
    map(const key_compare& comp) : base(comp) {}
    template <class InputIterator> map(InputIterator f, InputIterator l) : base(f, l) {}
    template <class InputIterator> map(InputIterator f, InputIterator l, const key_compare& comp) : base(f, l, comp) {}
    map(const base& other) : base(other) {}
};

// TODO: similar code for other container types
Run Code Online (Sandbox Code Playgroud)

你看到的基本上是STL类,其方法是返回size_type,只返回'int'.需要构造函数,因为它们不是继承的.

如果您在现有代码库中看到这样的解决方案,您会如何看待这个开发人员?

你会想到'whaa,他们正在重新定义STL,这是一个巨大的WTF!',或者你认为这是一个很好的简单解决方案来防止错误和提高可读性.或者你可能更愿意看到我们花了一半左右的时间来改变所有这些循环以使用std :: vector <> :: iterator?

(特别是如果这个解决方案与禁止使用无符号类型除了原始数据(例如unsigned char)和位掩码之外的任何东西.)

fiz*_*zer 7

不要公开从STL容器中派生.它们具有非虚拟析构函数,如果有人通过指向基类删除其中一个对象,则会调用未定义的行为.如果必须从向量派生,请私下进行,并使用using声明公开需要公开的部分.

在这里,我只使用a size_t作为循环变量.它简单易读.评论说使用int索引将你公开为n00b 的海报是正确的.但是,使用迭代器循环遍历向量会将您暴露为稍微有点经验的n00b - 一个没有意识到向量的下标运算符是常量时间的n00b.(vector<T>::size_type准确,但不必要的详细IMO).

  • 实现目标的一种方法是size_t足以保存任何对象的大小,并且保证向量由连续存储支持,因此size_type <= size_t (2认同)

Joh*_*itb 3

我制作了这个社区维基...请编辑它。我不再同意反对“int”的建议。我现在认为还不错。

是的,我同意理查德的观点。您永远不应该'int'在像这样的循环中使用计数变量。以下是您可能希望如何使用索引执行各种循环(尽管没有什么理由这样做,但有时这可能很有用)。

向前

for(std::vector<int>::size_type i = 0; i < someVector.size(); i++) {
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

落后

你可以这样做,这是完美定义的行为:

for(std::vector<int>::size_type i = someVector.size() - 1; 
    i != (std::vector<int>::size_type) -1; i--) {
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

很快,随着 c++1x(下一个 C++ 版本)的顺利推出,您可以这样做:

for(auto i = someVector.size() - 1; i != (decltype(i)) -1; i--) {
    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

递减到 0 以下将导致 i 回绕,因为它是无符号的。

但未签名会让 bug 大量涌入

这永远不应该成为以错误的方式(使用)的论点'int'

为什么不使用上面的 std::size_t ?

C++ 标准在 中定义23.1 p5 Container RequirementsT::size_type对于Tsome Container,该类型是某个实现定义的无符号整型。现在,使用上面的std::size_t方法i会让错误悄悄地侵入。ifT::size_type小于或大于std::size_t,那么就会溢出i,或者甚至达不到(std::size_t)-1if someVector.size() == 0。同样,循环的条件也将被完全破坏。