3

I'm currently playing around with constexpr arrays, and i noticed that i can't get the following (valid) code to compile under MSVC 19.15.26726 with /std:c++17 or /std:c++latest:

#include <array>
using array_type = std::array<unsigned int, 3>;
using iterator_type = array_type::const_iterator;
constexpr array_type arr{ { 1,2,3 } };

constexpr iterator_type getIteratorBefore(iterator_type it) {
    return std::prev(it);
}

constexpr iterator_type test = getIteratorBefore(arr.end());

Ignoring all the highlighting errors and the error that says std::array is ambigous (seems to conflict with some weird array() function in the same file) that I'm getting from IntelliSense, I get the following compiler errors in the last line:

error C4146: unary minus operator applied to unsigned type, result still unsigned
error C4308: negative integral constant converted to unsigned type
warning C4307: '+': integral constant overflow

It compiles fine in compiler explorer under gcc (x86-64 gcc (trunk)) and MSVC (x86-64 edit: MSVC Pre 2018 with /std:c++17 works) (didn't test the others).

I'm seriously out of ideas. The same code compiles when i put it in a main method, so it seems to be an issue with the constexpr scope.

8
  • 1
    Looks like a simple bug in MSVC. Should be compilable in C++17 mode. Commented Aug 24, 2018 at 15:36
  • C4146 and C4308 are comming up as warnings for me with the same version Commented Aug 24, 2018 at 15:38
  • @SergeyA Hmm seems you used the wrong compiler flag (see last output statement). It's /std:c++17 for MSVC. Doesn't matter though, because the version i mentioned in the post does not know the argument (?). It still thinks that we are in c++11, where non literal types were not allowed as constexpr function return types. Commented Aug 24, 2018 at 15:41
  • I compiled using /std:c++latest and the code compile and test points to 3. The two errors you show come up as warnings for me but the code does compile. Commented Aug 24, 2018 at 15:43
  • @JulianWiesler I was referring to the code, not the compiler. The posted code is a valid C++17 code, and if MSVC fails to compile it, it is the bug in MSVC. Commented Aug 24, 2018 at 15:48

1 Answer 1

3

Repro

I can reproduce with VS 2017 15.8.1 and also with the latest 15.9.0 Preview 1.0 with /std:c++17 or /std:c++latest.

The problem only occurs if _ITERATOR_DEBUG_LEVEL does not equal 0, typically in debug configurations.

Cause

Spelunking through the STL code coming with MSVC, we can see that _Array_const_iterator has two different implementations depending on _ITERATOR_DEBUG_LEVEL. If _ITERATOR_DEBUG_LEVEL does not equal 0, the iterator stores a base pointer to the array and an index variable _Idx of type std::size_t. Otherwise it stores just a pointer.

Part of the problem is caused by _Array_const_iterator::operator+=(), which is indirectly called by std::prev() with argument value of -1:

_CONSTEXPR17 _Array_const_iterator& operator+=(const ptrdiff_t _Off)
    {   // increment by integer
    _Verify_offset(_Off);
    _Idx += _Off;          //<-- error C4308
    return (*this);
    }

Error C4308 is caused because _Idx is unsigned, whereas _Off is signed and the actual (literal) value of _Off is negative.

Even simpler test case:

constexpr unsigned Test(unsigned x, int d) { x += d; return x; }
constexpr auto test1 = Test( 5, -1 );   //<-- error C4308
constexpr auto test2 = Test( 5, 1 );    //<-- OK

The assignment to test1 produces error C4308 and warning C4307 too. I'm not sure about C4146, it's propably just a follow-up error.

MSVC seems to be more strict than GCC when signed and unsigned types are mixed in a constexpr context.

The solution would propably be for MSFT to change the type of member variable _Idx to ptrdiff_t. Feel free to file a bug :-).

Workaround

Define _ITERATOR_DEBUG_LEVEL = 0 or replace the std::prev() call:

constexpr iterator_type getIteratorBefore(iterator_type it) {
    return --it;
}
Sign up to request clarification or add additional context in comments.

1 Comment

That helped, thanks. But why did they print just this error line? In normal template code you get at least a pseudo-stacktrace to the actual template code, that causes the error. Would be nice to have in constexpr as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.