Resource handle const correctness

Viewed 60

I have sometimes problems with understanding const correctness, especially if it is the logical constness of an object. Let's say I have a class that is a handle for some resource. If it is a const handle then I don't want to allow the resource to be modified through it. It doesn't own this resource, it is kinda like a "view" for it. If I copy it, I don't create a new resource.

template<typename T>
class Handle
{
private:
    T *res = nullptr; // just placeholder value...

public:
    //constructors and other code...

    const auto& getRes() const  {return *res;}
    auto& getRes()              {return *res;}

};

Here I have a problem. If I make a non-const copy from this const handle, I can modify the resource.

void func(const Handle<int>& res)
{
    //res.getRes() = 10; // Error. good.
    auto res_copy = res;
    res_copy.getRes() = 10; // Not good.
}

I just assume that if I pass something to a function as const, then somehow I should be able to enforce this logical constness. Or maybe I'm thinking it wrong. I know the standard library uses things like const-iterator / iterator or string-view that is always const but don't know how I can apply that to this case.

I can make a ConstHandle or convert a Handle<int> to Handle<const int> but then I will have to specify in a function the constness of the handle and constness of the resource, ex:

void func(const Handle<const int>& res)

And maybe it wouldn't work well if I just want to pass a const auto& handle to the function, or a const HandleType concept. Maybe I'm overthinking but I just want this to be sorted out in my head.

1 Answers

I just assume that if I pass something to a function as const, then somehow I should be able to enforce this logical constness.

This can lead to surprising behaviour or a constness that is un-enforceable. As you noticed, you would just have to copy the handle to have a mutable one.

This is the same problem as const pointers not pointing to const data.

I would suggest you to follow what the standard is doing with std::span. You need to use std::span<const int> if you want the data to be const.

In your case, that would indeed be Handle<const int> if you want to enforce constness on the thing that that handle is pointing to.


You could also have a ConstHandle, but since you already have a template parameter to specify T, I would use that for simplicity. For ease of use you could also do that:

template<typename T>
using ConstHandle = Handle<T const>;
Related