I stumbled upon an unexpected behavior of a shared pointer I'm using.
The shared pointer implements reference counting and detaches (e.g. makes a copy of), if neccessary, the contained instance on non-const usage.
To achieve this, for each getter function the smart pointer has a const
and a non-const
version, for example: operator T *()
and operator T const *() const
.
Problem: Comparing the pointer value to nullptr
leads to a detach.
Expected: I thought that the comparison operator would always invoke the const version.
Simplified example:
(This implementation doesn't have reference counting, but still shows the problem)
#include <iostream>
template<typename T>
class SharedPointer
{
public:
inline operator T *() { std::cout << "Detached"; return d; }
inline operator const T *() const { std::cout << "Not detached"; return d; }
inline T *data() { std::cout << "Detached"; return d; }
inline const T *data() const { std::cout << "Not detached"; return d; }
inline const T *constData() const { std::cout << "Not detached"; return d; }
SharedPointer(T *_d) : d(_d) { }
private:
T *d;
};
int main(int argc, char *argv[])
{
SharedPointer<int> testInst(new int(0));
bool eq;
std::cout << "nullptr == testInst: ";
eq = nullptr == testInst;
std::cout << std::endl;
// Output: nullptr == testInst: Detached
std::cout << "nullptr == testInst.data(): ";
eq = nullptr == testInst.data();
std::cout << std::endl;
// Output: nullptr == testInst.data(): Detached
std::cout << "nullptr == testInst.constData(): ";
eq = nullptr == testInst.constData();
std::cout << std::endl;
// Output: nullptr == testInst.constData(): Not detached
}
Question 1: Why is the non-const version of the functions called when it should be sufficient to call the const version?
Question 2: Why can the non-const version be called anyways? Doesn't the comparison operator (especially comparing to the immutable nullptr
) always operate on const references?
For the record:
The shared pointer I'm using is Qt's QSharedDataPointer
holding a QSharedData
-derived instance, but this question is not Qt-specific.
Edit:
In my understanding, nullptr == testInst
would invoke
bool operator==(T const* a, T const* b)
(Because why should I compare non-const pointers?)
which should invoke:
inline operator const T *() const
Further questions:
- Why isn't it the default to use the const operator?
- Is this because a function cannot be selected by the type of the return value alone?
=> This is answered in Calling a const function rather than its non-const version
- Is this because a function cannot be selected by the type of the return value alone?
So this question boils down to:
- Why doesn't the default implementation of the comparison operator take the arguments as const refs and then call the const functions?
- Can you maybe cite a c++ reference?