Does clang++ 12 support C++20 std::construct_at?

1.6k views Asked by At

I'm playing with recently approved C++20 standard features std::construct_at() and trying to get more familiar with them...

I've built an example based on the example from CppReference.com:

#include <memory>

struct S {
    int a;
    int b;
};

int main() {
    std::allocator<S> alloc;
    S * s = alloc.allocate(1);
    std::construct_at(s, 42, 43); // GCC 10.2 OK Clang 12 NOT
    std::destroy_at(s);
    alloc.deallocate(s, 1);
    s = nullptr;
}

The code above builds fine with the latest stable GCC: gcc-10.2 test.cpp -std=c++2a -lstdc++

But I can't get it to compile with Clang 12 (trunk) or 10 (stable). clang-12 test.cpp -std=c++20 -stdlib=libc++ with error:

test.cpp:11:5: error: no matching function for call to 'construct_at'
    std::construct_at(s, 42, 43); // GCC 10.2 OK Clang 12 NOT
    ^~~~~~~~~~~~~~~~~
/usr/local/clang/bin/../include/c++/v1/memory:903:16: note: candidate template ignored: substitution failure [with _Tp = S, _Args = <int, int>]: no matching constructor for initialization of 'S'
constexpr _Tp* construct_at(_Tp* __location, _Args&& ...__args) {
               ^
1 error generated.

Am I using the parameter pack incorrectly? Clang C++20 feature list doesn't seem to indicate it is supported yet? I've tried with the GCC toolchain in Clang and same results: --gcc-toolchain=/usr/local/gcc-10.2.0

I obtain similar falures with online compiler explorers.

If I explicitly construct an S by initializer-list (which may very well defeat the purpose of the in-place construct) std::construct_at(s, S{42, 43}); then the program compiles but fails at linking.

Is this approach constructing a temporary defeating the construct_at purpose? What if I tack on a std::move? std::construct_at(s, std::move(S{42, 43})); Online compiler explorers seem to indicate more assembly required with the move, perhaps there's an elision without it (and I created a pessimism by adding the move)?

The linking error is:

/usr/bin/ld: /tmp/test-b07239.o: in function `std::__1::__throw_length_error(char const*)':
test.cpp:(.text._ZNSt3__120__throw_length_errorEPKc[_ZNSt3__120__throw_length_errorEPKc]+0x12): undefined reference to `__cxa_allocate_exception'
/usr/bin/ld: test.cpp:(.text._ZNSt3__120__throw_length_errorEPKc[_ZNSt3__120__throw_length_errorEPKc]+0x30): undefined reference to `typeinfo for std::length_error'
/usr/bin/ld: test.cpp:(.text._ZNSt3__120__throw_length_errorEPKc[_ZNSt3__120__throw_length_errorEPKc]+0x3a): undefined reference to `std::length_error::~length_error()'

Apparently -stdlib=libc++ should be used linked with an ABI: -lstdc++ solve the linking error while -lc++abi still misses:

/usr/bin/ld: /tmp/test-bb0950.o: in function `std::length_error::length_error(char const*)':
test.cpp:(.text._ZNSt12length_errorC2EPKc[_ZNSt12length_errorC2EPKc]+0x23): undefined reference to `std::logic_error::logic_error(char const*)'

Could it be I am missing yet another library for the logic_error?

Back to the key questions at hand:

  • Does Clang supports std::construct_at(s, 42, 43); ?
  • Is there a performance hit to use the initializer-list construction? With/o move?
1

There are 1 answers

3
Nicol Bolas On BEST ANSWER

This has nothing to do with construct_at. S is an aggregate; outside of its default and copy/move constructors, it has no constructors to be called. So S can only be constructed with values through aggregate initialization.

This normally requires a braced-init-list (eg: S{3, 5}). But C++20 includes a feature that allows aggregate initialization to work through constructor syntax (eg: S(3, 5)) so long as the parameters wouldn't call a default or copy/move constructor (in which case, it'll call one of them).

But Clang doesn't implement that feature as of yet. So construct_at fails to construct the aggregate.