Replies: 2 comments 5 replies
-
This seems to at least allow for types that are consteval capable relocatable test if destruction can be skipped #include <concepts>
#include <type_traits>
#include <memory>
#include <vector>
#include <string>
template <typename T, typename = void>
struct is_constexpr_constructible_t : std::false_type {};
template <typename T>
struct is_constexpr_constructible_t<T, std::void_t<decltype(T())>>
: std::bool_constant<noexcept(T()) > {};
template <typename T>
inline constexpr bool is_constexpr_constructible_v = is_constexpr_constructible_t<T>::value;
template <typename T>
concept constexpr_constructible = is_constexpr_constructible_v<T>;
template<typename T>
constexpr bool simple_relocation_test()
{
std::allocator<T> alloc;
auto ptr{alloc.allocate(1)};
std::construct_at(ptr);
T tmp{std::move(*ptr)};
alloc.deallocate(ptr, 1);
return true;
}
template<typename T>
consteval auto is_reclocation_non_destructible() noexcept -> bool
{
if constexpr(std::is_trivially_destructible_v<T>)
return true;
else if constexpr (constexpr_constructible<T>)
{
return simple_relocation_test<T>();
}
return false;
}
template<typename T>
concept simple_reclocatable =is_reclocation_non_destructible<T>();
struct constexpr_type
{
constexpr constexpr_type() = default;
constexpr ~constexpr_type() = default;
};
struct non_constexpr_type
{
non_constexpr_type() {}
~non_constexpr_type() {}
};
struct non_trivial_t
{
int * ptr_;
constexpr non_trivial_t() : ptr_{new int()} {}
constexpr non_trivial_t(non_trivial_t const & rh) : ptr_{new int(*rh.ptr_)} {}
constexpr ~non_trivial_t() { delete ptr_; }
};
static_assert(constexpr_constructible<constexpr_type>);
static_assert(!constexpr_constructible<non_constexpr_type>);
static_assert(!constexpr_constructible<non_trivial_t>);
static_assert(simple_reclocatable<int>);
static_assert(simple_reclocatable<std::string>);
static_assert(simple_reclocatable<std::vector<int>>);
static_assert(simple_reclocatable<constexpr_type>);
static_assert(!simple_reclocatable<non_constexpr_type>);
static_assert(!simple_reclocatable<non_trivial_t>); |
Beta Was this translation helpful? Give feedback.
-
IIUC, the instances of
with:
This replaces ((move-construct + move-assign) + copy-assign) with (memmove + copy-construct).
Are you concerned about making your container work both with Re: the rest of your comments, I'll have to read them again in a few days when I have more time. |
Beta Was this translation helpful? Give feedback.
-
@Quuxplusone I would like to discuss relocation with You
I prepared everything with minimizing in mind non trivial copy and move to only types that are not trivially moveable/copyable and for cases when they are noxecept. As I cannot safely move elements if move operator of elements is possibly throwing. In such case I had to make deep copy and destroy (when not trivially destructible).
So everything is built from blocks of logic depending on attributes.
In some places when I am moving elements from one to other containers there is option to skip destruction of moved elements if theirs destructor is trivial, hovever there are some types that have destructor not trivial by after move we know that they are not needing destruction as they are empty. example after moving small vector that was dynamically allocated moved from vector will be empty. So I prepared in such places functions relocate_...
The only thing (mostly) I have to do is replace std::is_trivialy_destructible in uninitialized functions with some attribute is trivialy relocatable.
I read https://quuxplusone.github.io/blog/2024/06/15/who-uses-trivial-relocation/ >5 times and I still can not figure out whats the difference from my perspective of the two approaches, my containers what I am missing...
were is the possible optimization I am missing with my approach to reduce destruction when it is not needed.
First of all I need stable implementation which is aiming at space reduction, small_vectors and static vectors are all about that compared to std::vector
Second is dynamic allocation reduction
Third is static_vector ability to use in interprocess memory as it is address independent (holding size instead of end pointer and having only in class storage)
Fourth is moving whenever possible instead of deep copy , so some kind of performance improvment.
Fifth would be relocation, already skipping destruction of trivially destructible, and still missing skipping destruction when object after move is in state it doesn't need destruction (empty after move)
So I am probably going with adl specialized function lookup
if some consteval bool adl_is_relocatable( T const * ) noexcept; returns true then will skip destruction.
Ill define it for my types, std ones and use is_trivially_destructible for default implementation of it.
Any comments, ideas, improvements to my approach ?
When You look at ex include/small_vectors/detail/vector_func.h
inline constexpr vector_outcome_e insert
when thre is reallocation it is already using uninitialized_relocate_n for non throwing So i just need to fix in inline constexpr void uninitialized_relocate_n conxtexpr condition to this adl ...
if constexpr(!std::is_trivially_destructible_v<value_type>)
std::destroy_at(std::addressof(*src));
Beta Was this translation helpful? Give feedback.
All reactions