I have a class which contains references, like:
class A {
A(B &b) : b(b) {} // constructor
B &b;
}
Sometimes b must be read-only, sometimes it is writeable. When I make a const A a(b); object, it's obvious that I want to protect the data inside it as const.
But - by accident - it's easy to make a non-const copy of the object which will make the data inside it vulnerable.
const A a(b); // b object protected here
A a_non_const(a);
a_non_const.b.non_const_function(...); // b not protected now
I think that I should somehow prevent copies of the object when it is const like this:
const A a(b);
const A a2(a); // OK!
A a_non_const(a); // Compiler error
Is this possible at all?
flaw in your code: your data isn't "protected" even with const
The const type qualifier manages access to the member functions of a type as well as the access to its members. Since your member B & b is a reference, const doesn't do much for you here: A reference cannot be changed after initialization either way. How you access the target of that reference isn't even considered:
const A a(b);
a.b.non_const_function(); // OOPS, no problem!
solution with templates
Instead of (ab)using the const type qualifier you could add a "flag" to your type, to differentiate between cases where you need to be able to have non-const access and case where you don't:
#include <type_traits>
struct B {
void danger() {
}
void all_fine() const {
}
};
template<bool Writeable>
struct A {
using BRef = typename std::conditional<Writeable, B &, B const &>::type;
BRef b;
A (BRef b) : b(b) {};
};
using ConstA = A<false>;
using NonConstA = A<true>;
int main() {
B b;
ConstA a(b);
//NonConstA nc_a(a);
ConstA another_a(a);
//another_a.b.danger();
another_a.b.all_fine();
NonConstA a2(b);
a2.b.danger();
}
With some std::enable_if you can then selectively enable / disable member functions of A depending on whether they need "writeable" b or not.
real solution: refactor your design
BUT I'd like to highlight this comment even more:
"Sometimes b must be read-only, sometimes it is writeable." All your problems stem from this weird duality. I suggest picking one set of semantics for your class, not two
From Lightness Races in Orbit
You should probably instead consider splitting your class such that you have a CommonA with functionality used by both a WriteableA and a NonWriteableA (the names are terrible, but I hope you understand what I mean).
You can do it for the heap:
static const A *constCopy(const A &a); // and of course implement it somewhere
Then you will not accidentally modify the object via the pointer you get (which has to be stored in const A *, otherwise the compiler will complain).
However it will not work with stack-based objects, as returning const A & of a local variable is a rather deadly action, and "const constructor" has not been invented yet (related: Why does C++ not have a const constructor?)
Related
Let's say I have a class:
class A{
B* b;
public:
A(B* pb):b(pb){}
}
And I make sure const method of A never modify the object pointed by b. So that it is safe to hold B const* when instance of A is const.
B const b;
A a(&b); // compile error
A const ca(&b); // compile error too. How can I allow this one
This is impossible—constructors can’t even tell if the object is (going to be) const, let alone control overload resolution with the information.
The standard workaround (often used for iterators) is to make A a template (even if it only ever has two specializations) and use A<const B> to handle that case. Yes, this is pretty poor: partly because it can delay some diagnostics, but more because there’s no automatic conversion from A<B> (nor can a const A<const B>& bind to an A<B>). The good news is that this works without any special effort even when A has methods that do mutate b (which are often const in this formulation); they won’t be instantiated unless used.
Basically I want to have a simple class that wrap a pointer (i.e. has a pointer member) and I don't know how to specify the constness of the constructor arguments. If I do this
class A {
public:
A(int *d) : data(d) {}
private:
int *data;
};
Then I can't do this
void func(const int *param) {
const A aInstance(param);
}
because the constructor doesn't take a const pointer.
I want to be able to construct const objects and take const pointers to do it, but I also want to be able to construct non-const objects that modify the pointer. How do I specify this class so that I can modify *data when it's relevant, but I can construct a const object what that is relevant?
Your constructor should take a pointer which points to a constant object:
A(const int *d) : data(d) {}
You have a key misconception here: The compiler is warning you that you're passing a pointer to a const int into an object which might try to change the int's value. Declaring const A doesn't fix that problem because, while data cannot change, the thing that data points to still can.
The solution you need has already been provided by others in the comments:
If A doesn't need to modify *data then you should declare it's member variable as const int *data.
Otherwise you'll need to provide two versions: A and const_A. You could type out two separate definitions or use templates so you only have to type it out once.
If you know that your usage is safe and you want to live dangerously then you could use const_cast to remove the constness and thwart the compiler's warning.
The only answer that seems to satisfactorily maybe answer the question is a comment from #JohnAuld who suggested I template the class thusly
template< typename data_t >
class A
{
public:
A(data_t *data) : d(data) {}
private:
data_t *d;
};
In theory this would work well to construct a constable object, and it even lets me create const and non-const methods for which compilation will fail if I try to assign something to the data pointed to by d for const objects. That's all fine and dandy. It even let's me do what I wanted:
void doSomething(const int *somePointer) {
A<const int> a(somePointer);
}
However, where it fails is to use const specifiers on the class. If I wanted to pass the subsequent const object to another function, it would fail:
void doSomethingToAnA(const A<int> &a);
void doSomethingToIntPointer(const int *aPointer) {
const A<int> aInstance(aPointer);
doSomethingToAnA(aInstance);//womp womp
}
basically const A<int> != A<const int>
I'm using a library that defines some data type classes usually implemented as tight wrappers around a std::vector<>. The type hierarchy is several layers deep, mostly only adding elaborated constructors.
My problem: the base class defines its std::vector as private (which is fine), but only adds an accessor method as const. The derived class doesn't even have access to it. The library looks like this (shorted for clarity):
template <class T> class BaseList
{
public:
BaseList (const T data0) : data_ (1) {
data_[0] = data0; }
const T & operator[] (const size_t nr) const {
// does out off bounds check here
return data_[nr]; }
private:
std::vector<T> data_;
}
class FancyClass : public BaseList<SomeEnumType>
{
public:
FancyClass (const SomeOtherEnumType data0)
: BaseList<SomeEnumType> ( static_cast<SomeEnumType> (data))
{}
}
Now as I see it, the const definition is completely bogus. No internal method relies on the vector really being constant, neither do I in my external code.
What I like to do is simply this:
strukt MyType {
FancyClass myData;
bool otherData;
}
int main() {
MyType storage = {FancyClass(0), false};
storage.myData[0] = 5;
}
Which of course doesn't work because of the const-ness. ("assignment of read-only location")
With the responsibility completely on my side: is there some const_cast magic I could do to make this structure writable?
The only other possibility I know would be to completely replicate the type hierarchy in my code, but this would still leave me with either a lot of casts or a toFancyClass() function to call whenever I interface library code.
Any Ideas? Thanks!
(Please don't comment on the code quality of the library. If I could change that, I wouldn't be asking this question ...)
Now as I see it, the const definition is completely bogus. No internal method relies on the vector really being constant, neither do I in my external code.
The const qualifier on a method doesn't mean it relies on the vector being constant. It means that the method will not modify the state of the object. It is added so that the following code will compile.
void f(const FancyClass a)
{
cout<<a[0];
}
The above code will not above compile without the const qualifiers on the [] method.
Anyway, the following should work
SomeEnumType & r = const_cast<SomeEnumType &>(storage.myData[0]);
r = b;
where b is an enum of type SomeEnumType
However if your storage object is actually a const object, then it will lead to undefined behaviour.
The simplest would be to add non-const version of the operator[]. Otherwise, you might cause an UB by casting away the constness with using const_cast.
You can throw away the constness like this :
int main() {
MyType storage = {FancyClass(0), false};
const_cast< SomeEnumType& >( storage.myData[0] ) = 5;
}
Normally operator[] comes in pairs of a mutable and a const version.. (See: Operator overloading)
Of course there is a const_cast-based solution, but it involves undefined behaviour (you're not allowed to change a value after casting away constness!)
I was surprised to find this "hole" in "const"ness:
#include <stdio.h>
class A
{
int r ;
public:
A():r(0){}
void nonconst()
{
puts( "I am in ur nonconst method" ) ;
r++;
}
} ;
class B
{
A a ;
A* aPtr ;
public:
B(){ aPtr = new A() ; }
void go() const
{
//a.nonconst() ; // illegal
aPtr->nonconst() ; //legal
}
} ;
int main()
{
B b ;
b.go() ;
}
So basically from const method B::go(), you can invoke the non-const member function (aptly named nonconst()) if object of type A is referenced by a pointer.
Why is that? Seems like a problem (it kind of was in my code, where I found it.)
When and object of type B is const, then all of its members are const, which means its two members are, for the duration of B::go(), effectively
A const a;
A * const aPtr;
The first is a constant object of type A, on which you can only call const member functions. The second, however, is a constant pointer to a non-constant A. You could not legally say aPtr = <anything> from within the function B::go(), since that would modify aPtr, which is constant.
A pointer to a constant A would be declared as A const* aPtr or const A* aPtr, which would then make calling the non-constant A::nonconst() illegal.
The "const-ness" of an object does not extend to other objects through pointers. In your example, the const part is either the entire object a, or the pointer aPtr. Because aPtr is a A * and not a const A *, you are allowed to call a non-const method.
If you change
A* aPtr ;
to
const A* aPtr ;
then you will not be able to call aPtr->nonconst().
Language definition of const
I was surprised to find this "hole" in "const"ness:
There is none.
const applies uniformly to all class members: in a const member function of class C, this has type const C *, so for a member C::mem declared as type T:
class C {
// ...
T mem;
};
this->mem has type const T.
Please take type to discern what is the declared type T and the corresponding const-qualified type for all the members.
Seems like a problem (it kind of was in my code, where I found it.)
Just because the systematic application of rules does not do what you expected does not mean there is a problem with the rules, it means there is a problem with your expectations.
You should write down your expectations to see that you expected a non uniform application if const to different types.
When you program, you have to reason logically. You should infer things, not expect them when there is no logical reason.
Using const correctly
Why is that?
Your classes being called A and B, it's pretty hard to understand what constitute logical state and what does not. ;) You ask a "moral" question (not a question only on legal/illegal C++ programs), and your code fragment has no "moral" value. If you actually post relevant code, we might make some "moral" judgements about it.
Logical state
You should declare const the functions that do not change the "logical state" of the object it's applied to.
It means you have to define what is the "logical state" of your class instances: it's an abstract concept, and only you can define it, because it's a high level concept. The "logical state" relates to the problem your class should solve.
Then you can discern which variables contribute to the logical state: does *(b.aPtr) contribute to the logical state of b?
Closely related questions
Do you know about the copy constructor?
About the copy assignment operator?
About the destructor?
Let's say you have a class
class C
{
int * i;
public:
C(int * v):i(v) {};
void method() const; //this method does not change i
void method(); //this method changes i
}
Now you may want to define const instance of this class
const int * k = whatever;
const C c1(k); //this will fail
but this will fail because of non-const int C's constructor C(int * v)
so you define a const int constructor
C(const int * v):i(v) {}; //this will fail also
But this will fail also since C's member "int * i" is non-const.
What to do in such cases? Use mutable? Casting? Prepare const version of class?
edit: After discussion with Pavel (below) I investigated this problem a bit. To me what C++ does is not correct. Pointer target should be a strict type, that means that you could not for example do the following:
int i;
const int * ptr;
ptr = & i;
In this case language grammar treats const as a promise not to change pointer's target. In addition int * const ptr is a promise not to change pointer value itself. Thus you have two places where const can be applied. Then you may want your class to model a pointer (why not). And here things are falling into pieces. C++ grammar provides const methods which are able to promise not to change field's values itself but there is no grammar to point out that your method will not change targets of your in-class pointers.
A workaround is to define two classes const_C and C for example. It isn't a royal road however. With templates, their partial specializations it's hard not to stuck into a mess. Also all possible arguments variations like const const_C & arg, const C & arg, const_C & arg, C & arg don't look pretty. I really don't know what to do. Use separate classes or const_casts, each way seems to be wrong.
In both cases should I mark methods which don't modify pointer's target as const? Or just follow traditional path that const method doesn't change object's state itself (const method don't care about pointer target). Then in my case all methods would be const, because class is modelling a pointer thus pointer itself is T * const. But clearly some of them modify pointer's target and others do not.
Sounds like you want an object that can wrap either int* (and then behave as non-const), or int const* (and then behave as const). You can't really do it properly with a single class.
In fact, the very notion that const applied to your class should change its semantics like that is wrong - if your class models a pointer or an iterator (if it wraps a pointer, it's likely to be the case), then const applied to it should only mean that it cannot be changed itself, and should not imply anything regarding the value pointed to. You should consider following what STL does for its containers - it's precisely why it has distinct iterator and const_iterator classes, with both being distinct, but the former being implicitly convertible to the latter. As well, in STL, const iterator isn't the same as const_iterator! So just do the same.
[EDIT] Here's a tricky way to maximally reuse code between C and const_C while ensuring const-correctness throughout, and not delving into U.B. (with const_cast):
template<class T, bool IsConst>
struct pointer_to_maybe_const;
template<class T>
struct pointer_to_maybe_const<T, true> { typedef const T* type; };
template<class T>
struct pointer_to_maybe_const<T, false> { typedef T* type; };
template<bool IsConst>
struct C_fields {
typename pointer_to_maybe_const<int, IsConst>::type i;
// repeat for all fields
};
template<class Derived>
class const_C_base {
public:
int method() const { // non-mutating method example
return *self().i;
}
private:
const Derived& self() const { return *static_cast<const Derived*>(this); }
};
template<class Derived>
class C_base : public const_C_base<Derived> {
public:
int method() { // mutating method example
return ++*self().i;
}
private:
Derived& self() { return *static_cast<Derived*>(this); }
};
class const_C : public const_C_base<const_C>, private C_fields<true> {
friend class const_C_base<const_C>;
};
class C : public C_base<C>, private C_fields<false> {
friend class C_base<C>;
};
If you actually have few fields, it may be easier to duplicate them in both classes rather than going for a struct. If there are many, but they are all of the same type, then it is simpler to pass that type as a type parameter directly, and not bother with const wrapper template.
Your example doesn't fail, k is passed by value. The member i is 'implicitly constant' as direct members of C can't be changed when the instance is constant.
Constness says that you can't change members after initialization, but initializing them with values in the initialization list is of course allowed - how else would you give them a value?
What doesn't work is invoking the constructor without making it public though ;)
update addressing updated question:
Yes, C++ forces you into some verboseness sometimes, but const correctness is a common standard behaviour that you can't just redefine without breaking expectations. Pavels answer already explains one common idiom, which is used in proven libraries like the STL, for working around this situation.
Sometimes you have to just accept that languages have limitations and still deal with the expectations of the users of the interface, even if that means applying an apparently sub-optimal solution.
Your question does not make sense. Where did you get all these "this will fail" predictions? None of them are even remotely true.
Firstly, it is completely irrelevant whether the constructor's parameter is declared const or not. When you are passing by value (as in your case) you can pass a const object as an argument in any case, regardless of whether the parameter is declared as const or not.
Secondly, from the constructor's point of view, the object is NOT constant. Regardless of what kind of object you are constructing (constant or not), from within the constructor the object is never constant. So there's no need for mutable or anything.
Why don't you just try compiling your code (to see that nothing will fail), instead of making strange ungrounded predictions that something "will fail"?
A const int* is not the same as a int* const. When your class is const, you have the latter (constant pointer to mutable integer). What you're passing is the former (mutable pointer to constant integer). The two are not interchangeable, for obvious reasons.
When you instantiate
const C c1(...)
Because c1 is const, its member i turns in to:
int* const i;
As someone else mentioned, this is called implicit const.
Now, later in your example, you attempt to pass a const int*. So your constructor is basically doing this:
const int* whatever = ...;
int* const i = whatever; // error
The reason you get an error is because you can't cast const to non-const. The 'whatever' pointer is not allowed to change the thing it points to (the int part is const). The 'i' pointer is allowed to change what it points to, but cannot itself be changed (the pointer part is const).
You also mention wanting your class to model a pointer. The STL does this with iterators. The model some implementations use is having a class called 'const_iterator' which hides the real pointer and only supplies const methods to access the pointed-to data. Then there's also an 'iterator' class which inherits from 'const_iterator', adding non-const overloads. This works nicely - it's a custom class which allows the same constness as pointers, where the types mirror pointers like so:
iterator -> T*
const iterator -> T* const
const_iterator -> const T*
const const_iterator -> const T* const
Hopefully that makes sense :)
OK here's what I have done so far. To allow inheritance after const version of class without const_casts or additional space overhead I created an union which basically looks like ths:
template <typename T>
union MutatedPtr
{
protected:
const T * const_ptr;
T * ptr;
public:
/**
* Conversion constructor.
* #param ptr pointer.
*/
MutatedPtr(const T * ptr): const_ptr(ptr) {};
/**
* Conversion to T *.
*/
operator T *() {return ptr;}
/**
* Conversion to const T *.
*/
operator const T *() const {return const_ptr;}
};
When MutatedPtr field is declared, it ends up so that in const methods const_ptr is returned, while non-const ones get plain ptr. It delegates method's const-ness to pointer target which makes sense in my case.
Any comments?
BTW you can of course do similar thing with non-pointer types or even methods, so it looks that introducing mutable keyword wasn't necessary(?)
I've run into the same unfortunate issue and after lamenting the lack of a const constructor in C++ I've come to the conclusion that two templatization is the best course, at least in terms of reuse.
A very simplified version of my case/solution is:
template< typename DataPtrT >
struct BaseImage
{
BaseImage( const DataPtrT & data ) : m_data( data ) {}
DataPtrT getData() { return m_data; } // notice that if DataPtrT is const
// internally, this will return
// the same const type
DataPtrT m_data;
};
template< typename DataPtrT >
struct DerivedImage : public BaseImage<DataPtrT>
{
};
There is a very unfortunate loss of class inheritance but in my case it was acceptable to make a sort of casting operator to be able to cast between const and non-const types with some explicit knowledge of how to do the conversion under the hood. That mixed with some appropriate use of copy constructors and/or overloaded dereference operator might get you to where you want to be.
template< typename OutTypeT, typename inTypeT )
image_cast< shared_ptr<OutTypeT> >( const shared_ptr<InTypeT> & inImage )
{
return shared_ptr<OutTypeT>( new OutTypeT( inImage->getData() ) );
}