When implementing operator[] how should I include bounds checking?

前端 未结 13 1941
清酒与你
清酒与你 2021-01-19 06:12

First of all I apologize for the long lead up to such a simplistic question.

I am implementing a class which serves as a very long 1 dimensional index on a space fil

相关标签:
13条回答
  • 2021-01-19 06:43

    If I were you I would follow the example set by the stl.

    In this case std::vector supplies two methods: at which is bounds checked and operator[] which is not. This allows the client to decide with version to use. I would definitely not use the % size(), as this just hides the bug. However bounds checking will add a lot of overhead for when iterating over a large collection, this is why it should be optional. Although I agree with other posters that the assert is a very good idea as this will only cause a performance hit in debug builds.

    You should also consider returning references and supplying const and not const versions. Here are the function declarations for std::vector:

    reference at(size_type _Pos);
    const_reference at(size_type _Pos) const;
    
    reference operator[](size_type _Pos);
    const_reference operator[](size_type _Pos) const;
    

    As a good rule of thumb if I am not sure how to specify an API I look for examples of how others specify similar APIs. Also when ever I use an API I try to judge or rate it, find the bits I like and dislike.

    0 讨论(0)
  • 2021-01-19 06:46

    The easiest solution is to do as C++ itself does. This limits the amount of surprises that your users will experience.

    C++ itself is fairly consistent. Both the built-in [] on pointers and std::vector::operator[] have undefined behavior if you use an out-of-bound array index. If you want bounds checking, be explicit and use std::vector::at

    Hence, if you do the same for your class, you can document the out-of-bound behavior as "standard".

    0 讨论(0)
  • 2021-01-19 06:54

    For me, this solution is unacceptable because you can be hiding a very hard to find bug. Throwing an out of range exception is the way to go, or at least put an assertion in the function.

    0 讨论(0)
  • 2021-01-19 06:54

    Another option is to let the caller choose the out-of-bounds policy. Consider:

    template <class OutOfBoundsPolicy>
    quint16 curvePoint::operator[](size_t index)
    {
        index = OutOfBoundsPolicy(index, dimensions);
        return point[index];
    }
    

    Then you could define several policies that the caller may choose. For example:

    struct NoBoundsCheck {
        size_t operator()(size_t index, size_t /* max */) {
            return index;
        }
    };
    
    struct WrapAroundIfOutOfBounds {
        size_t operator()(size_t index, size_t max) {
            return index % max;
        }
    };
    
    struct AssertIfOutOfBounds {
        size_t operator()(size_t index, size_t max) {
            assert(index < max);
            return index % max;
        }
    };
    
    struct ThrowIfOutOfBounds {
        size_t operator()(size_t index, size_t max) {
            if (index >= max) throw std::domain_error;
            return index;
        }
    };
    
    struct ClampIfOutOfBounds {
        size_t operator()(size_t index, size_t max) {
            if (index >= max) index = max - 1;
            return index;
        }
    };
    
    0 讨论(0)
  • 2021-01-19 06:57

    Having an operator[] that never fails sounds nice, but may hide bugs later on, if a calling function uses an illegal offset, finds a value from the beginning of the buffer, and proceeds as if that is a valid value.

    0 讨论(0)
  • 2021-01-19 06:58

    Your solution would be nice if you were providing access to the points of an elliptic shape. But it will lead to very nasty bugs if you use it for arbitrary geometric functions, because you knowingly provide a false value.

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