[libcxx-commits] [PATCH] D116078: [libc++][ranges] Implement `construct_at` and `destroy{, _at}`.

Arthur O'Dwyer via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Tue Dec 21 06:16:44 PST 2021


Quuxplusone added a comment.

LprettyGTM; just nits.



================
Comment at: libcxx/include/__memory/ranges_construct_at.h:48
+  constexpr _Tp* operator()(_Tp* __location, _Args&& ...__args) const {
+    return _VSTD::construct_at(__location, _VSTD::forward<_Args>(__args)...);
+  }
----------------
As always, I'd feel better if this were
```
return ::new (_VSTD::__voidify(*__location)) _Tp(_VSTD::forward<_Args>(__args)...);
```
(for obvious-correctness, simplicity of implementation, and efficiency-at-`-O0`) but I suppose the extra-indirection way has the minor benefit that if we ever need to do any special magic to make `construct_at` constexpr-friendly, we'll only need to do it in one place? (For better or worse, I guess. If it later turned out that that magic was //required// to happen only in `construct_at` and not in `ranges::construct_at`, then we'd have to go back to the simple `::new`-expression here anyway.)


================
Comment at: libcxx/include/__memory/ranges_construct_at.h:57
+inline constexpr auto construct_at = __construct_at::__fn(__function_like::__tag());
+
+} // namespace __cpo
----------------
You've got an extra blank line here (and line 79); and please compare to other CPO definitions to make sure you're consistent with the indentation.
I recommend looking on the right-hand side here: https://reviews.llvm.org/D115607#change-mUEJDUqE3FiU


================
Comment at: libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp:67-76
+  // Explicit multiargument constructor.
+  {
+    Foo f;
+
+    Foo* result = std::ranges::construct_at(&f, 42, 123.45, 'z');
+    assert(result == &f);
+    assert(f.x == 42);
----------------
Let's make this
```
struct Foo {
    int a_;
    int b_;
    explicit Foo(int a, int b) : a_(b), b_(a) {}
    Foo(std::initializer_list<int>);
    void operator&() const = delete;
    void operator,(auto&&) const = delete;
};
~~~
Foo f(0, 0);  // or auto f = Foo(0, 0);
Foo *result = std::ranges::construct_at(std::addressof(f), 1, 2);
assert(result == std::addressof(f));
assert(result.a == 2);
assert(result.b == 1);
```
The major thing here is that I added `initializer_list` to make sure the library is correctly constructing with `Foo(1,2)` instead of `Foo{1,2}`. Then I threw in the deleted `&` and `,` overloads as an afterthought; I'm not really attached to them, but they seemed low-cost enough to justify their low benefit.


================
Comment at: libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp:109-111
+// Make sure std::construct_at SFINAEs out based on the validity of calling the constructor, instead of hard-erroring.
+template <typename T, typename = decltype(std::ranges::construct_at((T*)nullptr, 1, 2) // missing arguments for Foo(...)
+                                          )>
----------------
I smell clang-format's meddling in this weird comment-inside-the-parens style. ;)
The comments are a bit confusing, especially coupled with the use of `nullptr` — we //do// check for `nullptr` inside `construct_at`, but that part is specifically //not// SFINAE-friendly. What I'd rather see here is something like
```
constexpr bool can_construct_at(auto&&... args)
  requires requires { std::ranges::construct_at(decltype(args)(args)...); }
  { return true; }

constexpr bool can_construct_at(auto&&... args)
  { return false; }

static_assert( can_construct_at((Foo*)nullptr, 1, 2));
static_assert(!can_construct_at((Foo*)nullptr, 1));
static_assert(!can_construct_at((Foo*)nullptr, 1, 2, 3));
static_assert(!can_construct_at(nullptr, 1, 2));
static_assert(!can_construct_at((int*)nullptr, 1, 2));
static_assert(!can_construct_at(contiguous_iterator<Foo*>(), 1, 2));
```


================
Comment at: libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp:126
+  return 0;
+}
----------------
I'd like to see some tests with array types, if that's supposed to work; or otherwise,
```
static_assert(!can_construct_at((int(*)[])nullptr));
static_assert(!can_construct_at((int(*)[2])nullptr));
static_assert(!can_construct_at((int(*)[2])nullptr, 1, 2));

// and function types while we're at it
static_assert(!can_construct_at((int(*)())nullptr));
static_assert(!can_construct_at((int(*)())nullptr, nullptr));
```


================
Comment at: libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/ranges_destroy_at.pass.cpp:51
+
+  friend void operator&(VirtualCountedBase) = delete;
+};
----------------
Passing a polymorphic type by value should be a red flag. Let's make this
`void operator&() const = delete;`
and remove it from the derived class (because the derived class will inherit this one).


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D116078/new/

https://reviews.llvm.org/D116078



More information about the libcxx-commits mailing list