acceptable fix for majority of signed/unsigned warnings?

后端 未结 7 570
清酒与你
清酒与你 2021-01-12 03:32

I myself am convinced that in a project I\'m working on signed integers are the best choice in the majority of cases, even though the value contained within can never be neg

相关标签:
7条回答
  • 2021-01-12 03:47

    Definitely use an iterator. Soon you will be able to use the 'auto' type, for better readability (one of your concerns) like this:

    for (auto i = someVector.begin();
         i != someVector.end();
         ++i)
    
    0 讨论(0)
  • 2021-01-12 03:50

    I made this community wiki... Please edit it. I don't agree with the advice against "int" anymore. I now see it as not bad.

    Yes, i agree with Richard. You should never use 'int' as the counting variable in a loop like those. The following is how you might want to do various loops using indices (althought there is little reason to, occasionally this can be useful).

    Forward

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

    Backward

    You can do this, which is perfectly defined behaivor:

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

    Soon, with c++1x (next C++ version) coming along nicely, you can do it like this:

    for(auto i = someVector.size() - 1; i != (decltype(i)) -1; i--) {
        /* ... */
    }
    

    Decrementing below 0 will cause i to wrap around, because it is unsigned.

    But unsigned will make bugs slurp in

    That should never be an argument to make it the wrong way (using 'int').

    Why not use std::size_t above?

    The C++ Standard defines in 23.1 p5 Container Requirements, that T::size_type , for T being some Container, that this type is some implementation defined unsigned integral type. Now, using std::size_t for i above will let bugs slurp in silently. If T::size_type is less or greater than std::size_t, then it will overflow i, or not even get up to (std::size_t)-1 if someVector.size() == 0. Likewise, the condition of the loop would have been broken completely.

    0 讨论(0)
  • 2021-01-12 03:53

    vector.size() returns a size_t var, so just change int to size_t and it should be fine.

    Richard's answer is more correct, except that it's a lot of work for a simple loop.

    0 讨论(0)
  • 2021-01-12 03:57

    While I don't think "use iterators, otherwise you look n00b" is a good solution to the problem, deriving from std::vector appears much worse than that.

    First, developers do expect vector to be std:.vector, and map to be std::map. Second, your solution does not scale for other containers, or for other classes/libraries that interact with containers.

    Yes, iterators are ugly, iterator loops are not very well readable, and typedefs only cover up the mess. But at least, they do scale, and they are the canonical solution.

    My solution? an stl-for-each macro. That is not without problems (mainly, it is a macro, yuck), but it gets across the meaning. It is not as advanced as e.g. this one, but does the job.

    0 讨论(0)
  • 2021-01-12 04:00

    Skip the index

    The easiest approach is to sidestep the problem by using iterators, range-based for loops, or algorithms:

    for (auto it = begin(v); it != end(v); ++it) { ... }
    for (const auto &x : v) { ... }
    std::for_each(v.begin(), v.end(), ...);
    

    This is a nice solution if you don't actually need the index value. It also handles reverse loops easily.

    Use an appropriate unsigned type

    Another approach is to use the container's size type.

    for (std::vector<T>::size_type i = 0; i < v.size(); ++i) { ... }
    

    You can also use std::size_t (from <cstddef>). There are those who (correctly) point out that std::size_t may not be the same type as std::vector<T>::size_type (though it usually is). You can, however, be assured that the container's size_type will fit in a std::size_t. So everything is fine, unless you use certain styles for reverse loops. My preferred style for a reverse loop is this:

    for (std::size_t i = v.size(); i-- > 0; ) { ... }
    

    With this style, you can safely use std::size_t, even if it's a larger type than std::vector<T>::size_type. The style of reverse loops shown in some of the other answers require casting a -1 to exactly the right type and thus cannot use the easier-to-type std::size_t.

    Use a signed type (carefully!)

    If you really want to use a signed type (or if your style guide practically demands one), like int, then you can use this tiny function template that checks the underlying assumption in debug builds and makes the conversion explicit so that you don't get the compiler warning message:

    #include <cassert>
    #include <cstddef>
    #include <limits>
    
    template <typename ContainerType>
    constexpr int size_as_int(const ContainerType &c) {
        const auto size = c.size();  // if no auto, use `typename ContainerType::size_type`
        assert(size <= static_cast<std::size_t>(std::numeric_limits<int>::max()));
        return static_cast<int>(size);
    }
    

    Now you can write:

    for (int i = 0; i < size_as_int(v); ++i) { ... }
    

    Or reverse loops in the traditional manner:

    for (int i = size_as_int(v) - 1; i >= 0; --i) { ... }
    

    The size_as_int trick is only slightly more typing than the loops with the implicit conversions, you get the underlying assumption checked at runtime, you silence the compiler warning with the explicit cast, you get the same speed as non-debug builds because it will almost certainly be inlined, and the optimized object code shouldn't be any larger because the template doesn't do anything the compiler wasn't already doing implicitly.

    0 讨论(0)
  • 2021-01-12 04:01

    Don't derive publicly from STL containers. They have nonvirtual destructors which invokes undefined behaviour if anyone deletes one of your objects through a pointer-to base. If you must derive e.g. from a vector, do it privately and expose the parts you need to expose with using declarations.

    Here, I'd just use a size_t as the loop variable. It's simple and readable. The poster who commented that using an int index exposes you as a n00b is correct. However, using an iterator to loop over a vector exposes you as a slightly more experienced n00b - one who doesn't realize that the subscript operator for vector is constant time. (vector<T>::size_type is accurate, but needlessly verbose IMO).

    0 讨论(0)
提交回复
热议问题