While implementing my own forward_list
class template, I saw that some of the methods such as resize()
require the type be DefaultInsertable. According to the documentation, DefaultInsertable
Specifies that an instance of the type can be default-constructed in-place by a given allocator.
I understand what DefaultConstructable means and I can enforce that my type T meets this requirement either by using the default_initializable
concept or with the following template:
template <typename = std::enable_if_t<(T(),T{},true),int*>>
where T is any type
But how could I verify that a type can be default-constructed in place by a given allocator?
The custom is_default_insertable
type trait, which is true if the type T
models DefaultInsertable for an allocator type A
.
To implement the condition, it is sufficient to verify whether the expression std::allocator_traits<A>::construct(std::declval<A&>(), std::declval<T*>())
is well-formed. The allocator type is not rebound because the construct()
member function does not depend on it.
namespace detail {
template <typename, typename, typename = void>
struct is_default_insertable
: std::false_type {};
template <typename T, typename A>
struct is_default_insertable<T, A, std::void_t<decltype(std::allocator_traits<A>::construct(std::declval<A&>(), std::declval<T*>()))>>
: std::true_type {};
}
template <typename T, typename A>
struct is_default_insertable
: detail::is_default_insertable<T, A> {};
template <typename T, typename A>
inline constexpr bool is_default_insertable_v = is_default_insertable<T, A>::value;
Alternatively, the default_insertable
concept, which is equivalent to the above type trait, can be implemented in the following way.
template <typename T, typename A>
concept default_insertable = requires(A& x, T* y)
{
std::allocator_traits<A>::construct(x, y);
};