6
votes

Take the following code:

#include <array>

constexpr std::array<int, 10> a{};
static_assert(std::next(std::begin(a)) == std::begin(a) + 1);

With -std=c++17 GCC compiles it flawlessly, but Clang complains that the expression is not an integral constant expression. It looks like that the problem is about the std::next which, however, should be constexpr in C++17.

Nevertheless, std::next is in the std library, not in the compiler itself, therefore there is something weird going on. And just to make things even better, the example compiles perfectly if you pass -stdlib=libc++ to Clang.

What is going on? Who is wrong and who is right?

EDIT

The issue seems to be related to clang being toolchain-ed against GCC 7.2 inside godbolt. If you add the --gcc-toolchain=/opt/compiler-explorer/gcc-snapshot parameter to the command line, everything works flawlessly. (Thanks to @einpoklum who reported the issue to godbolt -- I have been slower ;) )

EDIT

For everyone who consider that an old compiler should work for actual standard, sorry to say that the consideration is meaningless. I am talking about the last versions of both GCC and Clang. And the problem is reproducible with the trunk version of both. Older compilers are not relevant for this question (MSVC behaviour would be interesting, instead).

2
It's because std::next arguments are (can be) evaluated during run time whereas static_assert expects a strictly constexpr condition. Just because the function is marked as constexpr doesn't mean it can't be invoked as a regular function. - Ron
@Ron In this case everything both a and the std::begin are constexpr, so it is fine -- everything marked constexpr can be evaluated at runtime, not the opposite - dodomorandi
Sorry @Ron, but since C++17 std::array::iterator is a literal type, and two literal values can be compared at compile-time. If not, static_assert(std::begin(a) != std::end(a)) should not compile, but it works flawlessly... - dodomorandi
Is iterator.operator+() a constexpr method? - underscore_d
Sorry again @Ron, as I said, it compiles flawlessly - dodomorandi

2 Answers

3
votes

Compiling in Wandbox

#include <array>

int main()
 {
   constexpr std::array<int, 10> a{};
   static_assert(std::next(std::begin(a)) == std::begin(a) + 1);
 }

with clang++ 4.0.1 and command line

clang++ prog.cc -Wall -Wextra -std=c++1z

I get the error

prog.cc:6:18: error: static_assert expression is not an integral constant expression
   static_assert(std::next(std::begin(a)) == std::begin(a) + 1);
                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.cc:6:18: note: non-constexpr function 'next<const int *>' cannot be used in a constant expression
/opt/wandbox/clang-4.0.1/include/c++/v1/iterator:599:1: note: declared here
next(_InputIter __x,
^
1 error generated.

But compiling with clang++ 5.0.0 (or 6.0.0 or 7.0.0 HEAD) with

clang++ prog.cc -Wall -Wextra -std=c++17

So it seems a low support for C++17 in clang++ 4.0.1 (and/or libraries used by clang++), corrected in the following versions.

-- EDIT --

The problem is confirmed (see einpoklum's answer) for clang++ 5.0.0 and clang++ 6.0.0 in gobold.

So I suppose the problem is the version of libstdc++: seems that gobold is using a version (a older one, I suppose) where std::next() isn't defined as constexpr where wandbox is using a version where std::next() is constexpr.

2
votes

With the Clang 6.0.0 and 5.0.0 version on GodBolt.org, your code does indeed fail to compile. But - with clang 5.0.0-3 on my system (Lubuntu 17.10), it seems to compile without errors.

This is weird behavior. So, perhaps not the best possible answer to your question, but faced with something like this I would report it on bugs.llvm.org and see what the clang/LLVM developers say.