Date: Mon, 7 Jul 2025 01:56:32 +0000
construct<T> is roughly equivalent to std::make_from_tuple, except that
- it is (or ought to be) a CPO, and
- its arguments are not packed.
I believe we should impose constraints similar to those of std::make_from_tuple to avoid dangling references and undesired casts.
Thanks,
F.v.S.
________________________________
From: Std-Proposals <std-proposals-bounces_at_[hidden]> on behalf of Avi Kivity via Std-Proposals <std-proposals_at_[hidden]>
Sent: Monday, July 7, 2025 4:24
To: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>; std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Cc: Avi Kivity <avi_at_[hidden]>
Subject: Re: [std-proposals] std::construct<T>
I wrote it up:
https://github.com/avikivity/cpp-std-proposals/blob/std-construct/std-construct.md#proposal-for-stdconstruct-function-object
On Wed, 2025-05-14 at 22:30 +0300, Avi Kivity wrote:
You're correct, std::construct should be a variable template with one parameter.
Regarding make_obj vs std::construct, obj is not a word, and I think the standard should try hard to use words. I think std::construct is consistent with std::construct_at.
btw are make_obj_using_allocator and std::construct related? The former allocates, the latter does not.
On Sun, 2025-05-11 at 12:38 -0400, Arthur O'Dwyer wrote:
Hi Avi,
FWIW, I'm not sure P3312 is going anywhere; notice it's still in EWGI, and the syntax/semantics proposed don't seem very C++-ish to me.
OTOH, your `std::construct` as written is very similar to the existing `std::make_obj_using_allocator<https://en.cppreference.com/w/cpp/memory/make_obj_using_allocator>`; the only difference is that make_obj_using_allocator takes a first argument of type std::allocator<T>, whereas yours omits that parameter.
OTOOH, your `std::construct` definitely doesn't achieve your purpose as written. You wrote a template of 1+K parameters, and then instantiated it with 1+0 arguments:
https://godbolt.org/z/8dnqThW1Y
template<class T, class... Args>
T std_construct(Args&&... args) {
return T(std::forward<Args>(args)...);
}
struct Arg1 {} arg1;
struct Arg2 {} arg2;
struct Arg3 {} arg3;
struct Type1 {
explicit Type1(Arg1, Arg2&, Arg3);
};
This code allows you to write:
Type1 (*pf3)(Arg1&&, Arg2&, Arg3&&) = std_construct<Type1, Arg1, Arg2&, Arg3>;
But it certainly does not allow you to write either:
Type1 (*pf0)() = std_construct<Type1>; // no, Type1 has no zero-argument constructor
Type1 (*pf3)(Arg1, Arg2&, Arg3) = std_construct<Type1>; // no, Typ1 has no zero-argument constructor and pf3 doesn't have the same function type as the zero-argument std_construct<Type1>
auto make_something = std::bind_front(std_construct<Type1>, ~~~); // certainly not
What you need for a generic-lambda-style thing is for std::construct<T> to be a callable object in its own right, like this:
https://godbolt.org/z/TTbf6cGdx
All Ranges adaptors are "partially applied templates" similar to what we're doing here.
Should make_obj_using_allocator be a "partially applicable" template like this, instead of a "fully applicable only" template as it currently is?
Should there be a new "partially applicable" template named std::make_obj and/or std::construct? (I would prefer the former name, FWIW, for consistency.)
Should there be a new core-language feature that permits automatic handling of the "partial application" of templates, somehow?
(But this last would run into trouble with backward compatibility, because your `std::construct<Type1>` from above is already legal C++ today: it just unambiguously does something different from what you want it to do. Making it automatically represent a "partial but not complete specification" of std::construct's template parameter list would change its meaning, possibly changing the meaning of existing code.)
my $.02,
–Arthur
On Sun, May 11, 2025 at 7:37 AM Avi Kivity via Std-Proposals <std-proposals_at_[hidden]<mailto:std-proposals_at_[hidden]>> wrote:
This is superceded by P3312 [1].
Instead of std::construct<T>(...), write (&T::T)(...).
[1] https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3312r1.pdf
On Sun, 2024-12-01 at 17:57 +0200, Avi Kivity wrote:
Functions and member functions are invocable, but constructors are not.
I propose to add
template <typename T, typename... Args>
T std::construct(Args&&... args) {
return T(std::forward<decltype(Args)>(args)...);
}
With this, we can pass a constructor where other functions can be
passed.
// build a callback that creates and returns a Type1 thing
std::function<Type1 (Arg3)> make_somthing =
std::bind_front(std::construct<Type1>, arg1, std::ref(arg2));
// transform a vector of ints to a vector of some other type
auto foo = some_container |
std::views::transform(std::construct<AnotherType>) |
std::ranges::to<std::vector>();
- it is (or ought to be) a CPO, and
- its arguments are not packed.
I believe we should impose constraints similar to those of std::make_from_tuple to avoid dangling references and undesired casts.
Thanks,
F.v.S.
________________________________
From: Std-Proposals <std-proposals-bounces_at_[hidden]> on behalf of Avi Kivity via Std-Proposals <std-proposals_at_[hidden]>
Sent: Monday, July 7, 2025 4:24
To: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>; std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Cc: Avi Kivity <avi_at_[hidden]>
Subject: Re: [std-proposals] std::construct<T>
I wrote it up:
https://github.com/avikivity/cpp-std-proposals/blob/std-construct/std-construct.md#proposal-for-stdconstruct-function-object
On Wed, 2025-05-14 at 22:30 +0300, Avi Kivity wrote:
You're correct, std::construct should be a variable template with one parameter.
Regarding make_obj vs std::construct, obj is not a word, and I think the standard should try hard to use words. I think std::construct is consistent with std::construct_at.
btw are make_obj_using_allocator and std::construct related? The former allocates, the latter does not.
On Sun, 2025-05-11 at 12:38 -0400, Arthur O'Dwyer wrote:
Hi Avi,
FWIW, I'm not sure P3312 is going anywhere; notice it's still in EWGI, and the syntax/semantics proposed don't seem very C++-ish to me.
OTOH, your `std::construct` as written is very similar to the existing `std::make_obj_using_allocator<https://en.cppreference.com/w/cpp/memory/make_obj_using_allocator>`; the only difference is that make_obj_using_allocator takes a first argument of type std::allocator<T>, whereas yours omits that parameter.
OTOOH, your `std::construct` definitely doesn't achieve your purpose as written. You wrote a template of 1+K parameters, and then instantiated it with 1+0 arguments:
https://godbolt.org/z/8dnqThW1Y
template<class T, class... Args>
T std_construct(Args&&... args) {
return T(std::forward<Args>(args)...);
}
struct Arg1 {} arg1;
struct Arg2 {} arg2;
struct Arg3 {} arg3;
struct Type1 {
explicit Type1(Arg1, Arg2&, Arg3);
};
This code allows you to write:
Type1 (*pf3)(Arg1&&, Arg2&, Arg3&&) = std_construct<Type1, Arg1, Arg2&, Arg3>;
But it certainly does not allow you to write either:
Type1 (*pf0)() = std_construct<Type1>; // no, Type1 has no zero-argument constructor
Type1 (*pf3)(Arg1, Arg2&, Arg3) = std_construct<Type1>; // no, Typ1 has no zero-argument constructor and pf3 doesn't have the same function type as the zero-argument std_construct<Type1>
auto make_something = std::bind_front(std_construct<Type1>, ~~~); // certainly not
What you need for a generic-lambda-style thing is for std::construct<T> to be a callable object in its own right, like this:
https://godbolt.org/z/TTbf6cGdx
All Ranges adaptors are "partially applied templates" similar to what we're doing here.
Should make_obj_using_allocator be a "partially applicable" template like this, instead of a "fully applicable only" template as it currently is?
Should there be a new "partially applicable" template named std::make_obj and/or std::construct? (I would prefer the former name, FWIW, for consistency.)
Should there be a new core-language feature that permits automatic handling of the "partial application" of templates, somehow?
(But this last would run into trouble with backward compatibility, because your `std::construct<Type1>` from above is already legal C++ today: it just unambiguously does something different from what you want it to do. Making it automatically represent a "partial but not complete specification" of std::construct's template parameter list would change its meaning, possibly changing the meaning of existing code.)
my $.02,
–Arthur
On Sun, May 11, 2025 at 7:37 AM Avi Kivity via Std-Proposals <std-proposals_at_[hidden]<mailto:std-proposals_at_[hidden]>> wrote:
This is superceded by P3312 [1].
Instead of std::construct<T>(...), write (&T::T)(...).
[1] https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3312r1.pdf
On Sun, 2024-12-01 at 17:57 +0200, Avi Kivity wrote:
Functions and member functions are invocable, but constructors are not.
I propose to add
template <typename T, typename... Args>
T std::construct(Args&&... args) {
return T(std::forward<decltype(Args)>(args)...);
}
With this, we can pass a constructor where other functions can be
passed.
// build a callback that creates and returns a Type1 thing
std::function<Type1 (Arg3)> make_somthing =
std::bind_front(std::construct<Type1>, arg1, std::ref(arg2));
// transform a vector of ints to a vector of some other type
auto foo = some_container |
std::views::transform(std::construct<AnotherType>) |
std::ranges::to<std::vector>();
Received on 2025-07-07 01:56:47