简体   繁体   中英

Strange Iterator Behavior in g++ 4.7.1

I originally wrote some code that uses std::array in Microsoft VS 2012. However, when porting it to g++ 4.7.1, some issues arose. It was narrowed down to what seems like a difference in iterator behavior between the platforms.

The following is a basic code sample that isolates the difference:

#include <vector>
#include <array>
#include <iostream>

const std::array<std::array<int, 3>, 3> scoring_segment_codes_ = {{
    {{1, 2, 0}}, {{3, 4, 0}}, {{99, 100, 0}}
}};

int main()
{
    // This works on: g++ (Debian 4.5.3-9) 4.5.3
    // and Visual Studio 2012 (Windows 8)
    // but NOT g++ (Debian 4.7.1-7) 4.7.1
    for (auto i = scoring_segment_codes_.at(1).begin(); *i != 0; ++i)
    {
        std::cout << "Bad: " << *i << std::endl;
    } 

    std::cout << std::endl;

    // works on all three
    for (unsigned i = 0; i < scoring_segment_codes_.at(1).size(); i++) {
        std::cout << "Good: " << scoring_segment_codes_.at(1)[i] << std::endl;
    }

    std::cout << std::endl;

    // works on all three
    auto bees = scoring_segment_codes_.at(1);
    for (auto i = bees.begin(); *i != 0; ++i)
    {
        std::cout << "Good: " << *i << std::endl;
    }
    return 0;
}

The output of this sample on g++ (Debian 4.5.3-9) 4.5.3 and Microsoft VC++ is:

Bad: 3
Bad: 4

Good: 3
Good: 4
Good: 0

Good: 3
Good: 4

This was what I was expecting the output to be. However, g++ (Debian 4.7.1-7) 4.7.1 produces the output:

Bad: 3
Bad: 32513
Bad: 6297664

Good: 3
Good: 4
Good: 0

Good: 3
Good: 4

The difference seems to be the way the iterator is retrieved. I had thought the result of auto i = scoring_segment_codes_.at(1).begin() is defined behavior. Is it not? Or is the problem something else completely?

If this is an issue with g++ 4.7.1, unfortunately this is a system I am stuck with. Is there anything I can get this to work on g++ 4.7.1? I know going through and making sure all the iterator usage is compatible is an option, but this seems like a very rich source of bugs.

As per my comment, the version of g++ I'm using is slightly different (4.7.0). Looking at the actual code for their array implementation gives this:

 #ifdef __EXCEPTIONS constexpr const_reference at(size_type __n) const { return __n < _Nm ? _M_instance[__n] : throw out_of_range(__N("array::at")); } //#else path 

From Constexpr GCC Wiki , we have that "A constant expression is noexcept even if it involves a call to a constexpr function that isn't declared noexcept.". Thus it looks like someone has erroneously labelled at as constexpr even though it can throw.

I'm not sure if this has been related as a bug report, but it certainly looks like a bug. A temporary fix is to use operator[] - but this looks like the reason operator[] works and at does not.

Edit: To answer the comment, modifying the standard library header is one possibility, although that certainly has problems.

The other is to perhaps create a wrapper like the following (note that I haven't tested this):

 template <typename T, std::size_t sz> const T& at(const std::array<T, sz>& arr, typename std::array<T, sz>::size_type index) { if(index < arr.size()) return arr[index]; throw std::out_of_range("array::at"); } 

Similarly for the non-const reference version. Note that this can call array.at() directly as the non-const version of at() is correct.

Neither of these solutions is perfect, but they may at least help you work around this bug.

2nd Edit: Looking into it more, apparently I'm wrong, it is legal to declare constexpr functions that throw utilizing a ternary operator. I'll leave the fix here because it works, but my analysis of what's going on is incorrect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM