Search code examples
c++stdclang++c++20compiler-explorer

Why does std::reverse used in consteval function does compile though not constexpr


Using this little code snippet on CompilerExplorer with std=c++20 for x86-64 clang 11.0.0 compiles and runs.

#include <algorithm>
#include <array>

consteval std::array<double,2> gof()
{
    std::array<double,2> x{ 3., 2.};
    std::reverse(begin(x),end(x));
    return x;
}

int
main(int argc, char *argv[])
{
    return gof().at(0);
}

But clang-tidy on CompilerExplorer with the same settings yields

clang-tidy (trunk) #1 with x86-64 clang 11.0.0

<source>:14:12: error: call to consteval function 'gof' is not a constant expression [clang-diagnostic-error]
    return gof().at(0);
           ^
<source>:7:5: note: non-constexpr function 'reverse<double *>' cannot be used in a constant expression
    std::reverse(begin(x),end(x));
    ^
<source>:14:12: note: in call to 'gof()'
    return gof().at(0);
           ^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/stl_algo.h:1180:5: note: declared here
    reverse(_BidirectionalIterator __first, _BidirectionalIterator __last)
    ^

What clang-tidy yields is what I expect. Why does clang handle it differently?


Solution

  • std::reverse is constexpr in C++20 (the question title suggests otherwise?), so nothing in gof() prevents it from being a valid constant expression.

    libstdc++ implements that constexpr change, while libc++ does not yet. So if you use libstdc++ (as clang does by default on Compiler Explorer), it'll work fine. But if you use libc++ explicitly, then it'll fail for the reason you expect (std::reverse isn't constexpr which makes gof() fail to be a constant expression). clang-tidy looks like it's using an older version of libstdc++ (9.2 from the error you pasted), which was before libstdc++ implemented the constexpr change.

    So basically, you're just straddling the bleeding edge of constexpr support here.