C++ Concept to match pointers and alikes


To fix an issue raised by our static checker in our of our templates, I had to develop a concept that would match pointers (raw pointers and smart pointers) but not C-arrays.

Let’s consider the following variables and test code:

int intValues[] = { 1, 2, 3, 4 };
std::vector<int> intVector = { 2, 2, 2, 2 };
int anInt = 42;
auto* anIntPtr = &anInt;
int* aNullPtr = nullptr;
auto aUniquePtr = std::make_unique<int>(666);
auto aSharedPtr = std::make_shared<int>(999);
auto anOptional = std::make_optional<int>(1234);

template <typename T> // the template we'll add constraints to
auto func(const T& o) 
{ ... }Langage du code : PHP (php)

The initial implementation would look like

template <typename T>
concept dereferenceable = requires(T ptr)
{
        { *ptr };
};Langage du code : HTML, XML (xml)

But this wouldn’t work as one can use array because it matches anIntPtr but also intValues, aUniquePtr, aSharedPtr and anOptional.

Adding a condition on casting to bool does not change this picture.

There is a type_traits matching raw pointers: std::is_pointer_v<T>. It is not a concept but can be used in the template requires clause.

There is, however, no equivalent for smart pointers. If there was, it should look like this:

template <typename T>
concept smart_pointer = requires(T ptr)
{
        { *ptr };
        { ptr.get() };
        { ptr.operator->() };
};Langage du code : HTML, XML (xml)

Requiring the get() method is mandatory to distinguish smart pointer from optional (both have the * and -> operators). By combining them in a single any_pointer concept, we match only the pointers:

template <typename T>
concept any_pointer = std::is_pointer_v<T> || smart_pointer<T>;Langage du code : HTML, XML (xml)

If we want to match optionals, we need another concept (as optional does not have a get() method) :

template <typename T>
concept optional_type = requires(T ptr)
{
        { *ptr };
        { ptr.has_value() } -> std::same_as<bool>;
        { ptr.value() };
};Langage du code : HTML, XML (xml)

Now if we want to combine the very generic dereferenceable and any of the more specific concepts, we need to exclude the specific ones in the template taking the dereferenceable, like this:

template <optional_type T> 
auto func(const T& opt) 
{ ... }

template <dereferenceable T> 
requires (!optional_type<T>) 
auto func(const T& ptr) 
{ ... }Langage du code : HTML, XML (xml)

Here’s the complete matching table:

dereferenceableis_pointer_vsmart_pointerany_pointeroptional_type
nullptrdecltype(nullptr)
intValuesint [4]Y
&intValues[0]int*YYY
intVector.data()int*YYY
&intVector[0]int*YYY
&anIntint*YYY
anIntPtrint*YYY
aNullPtrint*YYY
aUniquePtrstd::unique_ptr<int>YYY
aSharedPtrstd::shared_ptr<int>YYY
anOptionalstd::optional<int>YY

Here is the complete code:

Compile it with different compile-time defines to see the effect of each concept.

Share

,

  1. Pas encore de commentaire.
(ne sera pas publié)


Les commentaires sont fermés.