Hello!
I have a strange problem with my pimpl implementation. What happens is that
const correctness is not respected by the compiler. Among two methods, one
const and one non-const, the non-const version ends up being called on a
const
object. Let me go straight to the code:
#include <iostream>
struct Geometry {};
class LineData
{
class LineDataImpl;
LineDataImpl& mImpl;
public:
LineData();
const Geometry& GetGeometry() const;
/* */ Geometry& GetGeometry() /* */;
};
class LineData::LineDataImpl
{
Geometry mGeometry;
public:
const Geometry& GetGeometry() const
{
std::cout << "Calling const overload" << std::endl;
return mGeometry;
}
Geometry& GetGeometry()
{
std::cout << "Calling non-const overload" << std::endl;
return mGeometry;
}
};
LineData::LineData()
: mImpl(*(new LineDataImpl))
{ }
const Geometry& LineData::GetGeometry() const
{
return mImpl.GetGeometry();
}
Geometry& LineData::GetGeometry()
{
return mImpl.GetGeometry();
}
int main()
{
const LineData lineData;
const Geometry& geometry = lineData.GetGeometry();
return 0;
}
This sample shows that despite my intentions of being const correct, the
compiler ends up calling the non-const overload. What is going on here?
I'm using Visual Studio 2008, SP1.
Thanks in advance!
--
Daniel Lidström
const is shallow. mImpl is const, but LineDataImpl object it refers to
is not.
The difference would be easier to see if mImpl were declared as
LineDataImpl*. Then if LineData object is const, its mImpl member would
be LineDataImpl* const - but not const LineDataImpl*. That is, the
pointer itself can't change (say, to point to a different object), but
the object it points to happily can.
--
With best wishes,
Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
>"Daniel Lidström" <som...@microsoft.com> wrote in message
>news:e80rJvg...@TK2MSFTNGP06.phx.gbl
>> I have a strange problem with my pimpl implementation. What happens
>> is that const correctness is not respected by the compiler. Among two
>> methods, one const and one non-const, the non-const version ends up
>> being called on a const
>> object. Let me go straight to the code:
>>
>> class LineData
>> {
>> class LineDataImpl;
>> LineDataImpl& mImpl;
>>
>> const Geometry& LineData::GetGeometry() const
>> {
>> return mImpl.GetGeometry();
>
>const is shallow. mImpl is const, but LineDataImpl object it refers to
>is not.
>
>The difference would be easier to see if mImpl were declared as
>LineDataImpl*. Then if LineData object is const, its mImpl member would
>be LineDataImpl* const - but not const LineDataImpl*. That is, the
>pointer itself can't change (say, to point to a different object), but
>the object it points to happily can.
Yes, we mean different things by "const reference" and "const pointer", and
the fact that this case reflects the "const pointer" concept is arguably a
bug in the language. That is, there is no such thing as "X& const x_ref",
but that's effectively what we have here. The const does not apply to the
referent, and the notion that the reference is an alias for the object
kinda falls apart here. This is in contrast to sizeof, which acknowledges
the meaninglessness of taking the size of a reference, which is not an
object, and applies it to the referent instead, which is an object.
--
Doug Harrison
Visual C++ MVP
Thanks for explaining the problem, Igor and Doug.
--
Daniel
I think the solution would be:
const Geometry& LineData::GetGeometry() const
{
const LineDataImpl& cmImpl = mImpl;
return cmImpl.GetGeometry();
}