Date: Wed, 18 May 2022 14:32:49 +0200
I am also thinking of another alternative. In the proposal I introduced the
tag type std::relocate_t:
namespace std { struct relocate_t {}; inline constexpr relocate_t
relocate; }
This tag type was mostly used to distinguish between otherwise ambiguous
overloads:
std::vector<T, Alloc>::push_back(const T&);
std::vector<T, Alloc>::push_back(T&&);
std::vector<T, Alloc>::push_back(std::relocate_t, T);
And you would relocate an object inside a vector using the last overload:
void foo(std::vector<T>& vec)
{
T obj;
vec.push_back(std::relocate, reloc obj);
}
I am thinking we could add a new class attribute [[reloc_parameter]]. Any
function that has an object with this attribute in its parameters, captured
by value, will force the function to use an ABI that allows it to relocate
any of its input parameters.
std::relocate_t could have that attribute: namespace std { struct
[[reloc_parameter]] relocate_t {}; }
And then any function that takes an 'std::relocate_t' by value as parameter
will be allowed to relocate all its input parameters. The impact is minimal
as, in the STL, std::relocate_t would always need to be passed around to
distinguish between the overloads that capture objects by reference.
That way STL classes can provide their own 'operator reloc()' member
function, without changing the ABI. The ABI change is opt-in, only if the
function has that std::relocate_t parameter. STL classes do not need to
have the [[reloc_parameter]] attribute, only std::relocate_t needs it.
Keep in mind that std::relocate_t is not required to be added to functions
to use the reloc operator on function parameters. If not present and the
ABI does not permit it, then reloc will fallback to std::move (and
destruction at caller-site).
A few examples:
void sink(T);
// x is guaranteed to be relocated using trivial relocation
// or its relocator if accessible
void fwd_to_sink(std::relocate_t, T x) { sink(reloc x); }
// x can be relocated using trivial relocation,
// its relocator if accessible and the ABI permits
// or simply moved as if by std::move.
void fwd_to_sink2(T x) { sink(reloc x); }
// same as fwd_to_sink2, std::relocate needs to be passed by value.
void fwd_to_sink3(std::relocate_t&, T x) { sink(reloc x); }
// Allowed even though using std::relocate is preferred
class [[reloc_parameter]] MyUserClass { /**/ };
void sink(MyUserClass);
// x is guaranteed to be relocated using trivial relocation
// or its relocator if accessible
void fwd_to_sink(MyUserClass x) { sink(reloc x); }
tag type std::relocate_t:
namespace std { struct relocate_t {}; inline constexpr relocate_t
relocate; }
This tag type was mostly used to distinguish between otherwise ambiguous
overloads:
std::vector<T, Alloc>::push_back(const T&);
std::vector<T, Alloc>::push_back(T&&);
std::vector<T, Alloc>::push_back(std::relocate_t, T);
And you would relocate an object inside a vector using the last overload:
void foo(std::vector<T>& vec)
{
T obj;
vec.push_back(std::relocate, reloc obj);
}
I am thinking we could add a new class attribute [[reloc_parameter]]. Any
function that has an object with this attribute in its parameters, captured
by value, will force the function to use an ABI that allows it to relocate
any of its input parameters.
std::relocate_t could have that attribute: namespace std { struct
[[reloc_parameter]] relocate_t {}; }
And then any function that takes an 'std::relocate_t' by value as parameter
will be allowed to relocate all its input parameters. The impact is minimal
as, in the STL, std::relocate_t would always need to be passed around to
distinguish between the overloads that capture objects by reference.
That way STL classes can provide their own 'operator reloc()' member
function, without changing the ABI. The ABI change is opt-in, only if the
function has that std::relocate_t parameter. STL classes do not need to
have the [[reloc_parameter]] attribute, only std::relocate_t needs it.
Keep in mind that std::relocate_t is not required to be added to functions
to use the reloc operator on function parameters. If not present and the
ABI does not permit it, then reloc will fallback to std::move (and
destruction at caller-site).
A few examples:
void sink(T);
// x is guaranteed to be relocated using trivial relocation
// or its relocator if accessible
void fwd_to_sink(std::relocate_t, T x) { sink(reloc x); }
// x can be relocated using trivial relocation,
// its relocator if accessible and the ABI permits
// or simply moved as if by std::move.
void fwd_to_sink2(T x) { sink(reloc x); }
// same as fwd_to_sink2, std::relocate needs to be passed by value.
void fwd_to_sink3(std::relocate_t&, T x) { sink(reloc x); }
// Allowed even though using std::relocate is preferred
class [[reloc_parameter]] MyUserClass { /**/ };
void sink(MyUserClass);
// x is guaranteed to be relocated using trivial relocation
// or its relocator if accessible
void fwd_to_sink(MyUserClass x) { sink(reloc x); }
Received on 2022-05-18 12:33:01