Date: Wed, 15 Mar 2023 13:23:08 +0100
Hi,
I would want to propose a new type trait, std::is_instance. Below, there is
a draft of the proposal to explain clearer my idea.
#Motivation
The C++ standard library supplies some useful type traits. Among them,
there are std::is_same, that provides the member constant value equal to
true if T and U name the same type (not ignoring cv-qualification), and
std::is_base_of, that provides the member constant value equal to true if
Derived is derived from Base or if both are the same non-union class
(ignoring cv-qualification). However, there is not a type trait that allows
to verify whether T is an instance of U.
Consider the following example:
template <typename T, typename Alloc>
struct NodeHandle
{
[...]
T& value() const noexcept
{ return *this->m_ptr; }
node_pointer m_ptr;
allocator_type m_alloc;
};
template <typename U typename V, typename Alloc>
struct NodeHandle<std::pair<U, V>, Alloc>
{
[...]
std::pair<U, V>& value() const noexcept
{ return *this->m_ptr; }
U& key() const noexcept
{ return this->m_ptr->first; }
V& mapped() const noexcept
{ return this->m_ptr->second; }
node_pointer m_ptr;
allocator_type m_alloc;
};
It is possible to make the code more compact by creating a base class from
which the class and its partial specialization inherit common members, but
this produces two immediate side effects: inheritance makes the class non
standard-layout; inheritance makes it more difficult to change the code in
the future. An alternative approach is using std::enable_if or, if
possible, C++20's constraints, like the following:
template <typename V, typename U>
requires std::is_same_v<std::remove_cvref_t<T>, std::pair<V, U>>
However, it can be less readable, and the more complex the class, the less
readable it is.
It would be helpful to enable the last two member functions if and only if
T is an instance of std::pair using std::enable_if or, if possible, C++20's
constraints. This technique would reduce the amount of code and limit the
noise without changing the entire design.
Thus, a solution may be the following:
template <typename T, typename Alloc>
struct NodeHandle
{
[...]
T& value() const noexcept
{ return *this->m_ptr; }
T::first_type& key() const noexcept
requires std::is_instance_v<T, std::pair>
{ return this->m_ptr->first; }
T::second_type& mapped() const noexcept
requires std::is_instance_v<T, std::pair>
{ return this->m_ptr->second; }
node_pointer m_ptr;
allocator_type m_alloc;
};
A custom solution may take advantage of one or more already-existing type
traits and combine them to create a custom type trait, but the creation of
that non-standard type trait is an additive work for the programmer and may
carry some bugs if the code is changed in the future or there are some
oversights.
#Proposed design
std::is_instance provides the member constant value equal to true if and
only if T is an instance (ignoring cv-qualification and reference) of class
U. Otherwise it is false.
An example of implementation is the following:
namespace detail
{
template <typename T, template <typename...> typename U>
struct is_instance
: false_type {};
template <typename... Ts, template <typename...> typename U>
struct is_instance<U<Ts...>, U>
: true_type {};
}
template <typename T, template <typename...> typename U>
using is_instance = detail::is_instance<remove_cvref_t<T>, U>;
template <typename T, template <typename...> typename U>
inline constexpr bool is_instance_v = is_instance<T, U>::value;
(Issue) The proposed implementation does not work with non-type template
parameters for the moment.
I would want to propose a new type trait, std::is_instance. Below, there is
a draft of the proposal to explain clearer my idea.
#Motivation
The C++ standard library supplies some useful type traits. Among them,
there are std::is_same, that provides the member constant value equal to
true if T and U name the same type (not ignoring cv-qualification), and
std::is_base_of, that provides the member constant value equal to true if
Derived is derived from Base or if both are the same non-union class
(ignoring cv-qualification). However, there is not a type trait that allows
to verify whether T is an instance of U.
Consider the following example:
template <typename T, typename Alloc>
struct NodeHandle
{
[...]
T& value() const noexcept
{ return *this->m_ptr; }
node_pointer m_ptr;
allocator_type m_alloc;
};
template <typename U typename V, typename Alloc>
struct NodeHandle<std::pair<U, V>, Alloc>
{
[...]
std::pair<U, V>& value() const noexcept
{ return *this->m_ptr; }
U& key() const noexcept
{ return this->m_ptr->first; }
V& mapped() const noexcept
{ return this->m_ptr->second; }
node_pointer m_ptr;
allocator_type m_alloc;
};
It is possible to make the code more compact by creating a base class from
which the class and its partial specialization inherit common members, but
this produces two immediate side effects: inheritance makes the class non
standard-layout; inheritance makes it more difficult to change the code in
the future. An alternative approach is using std::enable_if or, if
possible, C++20's constraints, like the following:
template <typename V, typename U>
requires std::is_same_v<std::remove_cvref_t<T>, std::pair<V, U>>
However, it can be less readable, and the more complex the class, the less
readable it is.
It would be helpful to enable the last two member functions if and only if
T is an instance of std::pair using std::enable_if or, if possible, C++20's
constraints. This technique would reduce the amount of code and limit the
noise without changing the entire design.
Thus, a solution may be the following:
template <typename T, typename Alloc>
struct NodeHandle
{
[...]
T& value() const noexcept
{ return *this->m_ptr; }
T::first_type& key() const noexcept
requires std::is_instance_v<T, std::pair>
{ return this->m_ptr->first; }
T::second_type& mapped() const noexcept
requires std::is_instance_v<T, std::pair>
{ return this->m_ptr->second; }
node_pointer m_ptr;
allocator_type m_alloc;
};
A custom solution may take advantage of one or more already-existing type
traits and combine them to create a custom type trait, but the creation of
that non-standard type trait is an additive work for the programmer and may
carry some bugs if the code is changed in the future or there are some
oversights.
#Proposed design
std::is_instance provides the member constant value equal to true if and
only if T is an instance (ignoring cv-qualification and reference) of class
U. Otherwise it is false.
An example of implementation is the following:
namespace detail
{
template <typename T, template <typename...> typename U>
struct is_instance
: false_type {};
template <typename... Ts, template <typename...> typename U>
struct is_instance<U<Ts...>, U>
: true_type {};
}
template <typename T, template <typename...> typename U>
using is_instance = detail::is_instance<remove_cvref_t<T>, U>;
template <typename T, template <typename...> typename U>
inline constexpr bool is_instance_v = is_instance<T, U>::value;
(Issue) The proposed implementation does not work with non-type template
parameters for the moment.
Received on 2023-03-15 12:23:22