Making a constructor 'const'.


S

SzH

Is it possible for a constructor to know if the object has been
initialised as a 'const' ot not? The example at at end of the message
illustrates what I would like to do.

I want to allow constructing a 'const Aref' from a 'const A' [1], so I
added another constructor to 'Aref'. But I don't want to allow creating
a non-const Aref from a 'const A' [2] to prevent accidental modification
of a const A by Aref::set().

I tried to make the second constructor 'const', but found that this is
impossible.


class A {
int data;
public:
A(int i) : data(i) { }

int read() const { return data; }
void set(int val) { data = val; }
};

class Aref {
A &ref;
public:
Aref(A &a) : ref(a) { }

// added this constructor to allow [1]:
Aref(const A &a) : ref(const_cast<A&>(a)) { }
// but it is not possible to declare a constructor const
// to disallow [2]!

int read() const { return ref.read(); }
void set(int val) { ref.set(val); }
};

int main() {
const A a = 1;
const Aref ar1 = a; // should be allowed [1]
Aref ar2 = a; // should be illegal [2]
}
 
Ad

Advertisements

A

Alf P. Steinbach

* SzH:
class A {
int data;
public:
A(int i) : data(i) { }

int read() const { return data; }
void set(int val) { data = val; }
};

class Aref {
A &ref;
public:
Aref(A &a) : ref(a) { }

// added this constructor to allow [1]:
Aref(const A &a) : ref(const_cast<A&>(a)) { }
// but it is not possible to declare a constructor const
// to disallow [2]!

int read() const { return ref.read(); }
void set(int val) { ref.set(val); }
};

int main() {
const A a = 1;
const Aref ar1 = a; // should be allowed [1]
Aref ar2 = a; // should be illegal [2]
}

Use two classes ARef and ARefConst.
 
S

SzH

Alf said:
Use two classes ARef and ARefConst.

This is what I would like to avoid (otherwise all const members of Aref
should be duplicated in ArefConst). It must also be possible to replace
an ArefConst by an Aref, but using inheritance/virtual functions is not
an option because of performance reasons.

Actually in my program A is an array of numerical values, but in some
circumstances it is preferable to treat certain ranges of an array
separately. For this purpose I made a slice (Aref) class that references
an array and simply allows to index a range of elements in it, e.g. it
is possible to "name" elements 10..20 of an array and index them from 0..10.

There were no problems until I tried to make slices writeable.

Any suggestions for implementing this?
 
A

Alf P. Steinbach

* SzH:
This is what I would like to avoid (otherwise all const members of Aref
should be duplicated in ArefConst). It must also be possible to replace
an ArefConst by an Aref,

Inheritance or conversion.

but using inheritance/virtual functions is not
an option because of performance reasons.
Measure.


Actually in my program A is an array of numerical values, but in some
circumstances it is preferable to treat certain ranges of an array
separately. For this purpose I made a slice (Aref) class that references
an array and simply allows to index a range of elements in it, e.g. it
is possible to "name" elements 10..20 of an array and index them from
0..10.

There were no problems until I tried to make slices writeable.

Any suggestions for implementing this?

Have you looked at std::valarray?
 
Ad

Advertisements

M

Michael DOUBEZ

SzH a écrit :
This is what I would like to avoid (otherwise all const members of Aref
should be duplicated in ArefConst). It must also be possible to replace
an ArefConst by an Aref, but using inheritance/virtual functions is not
an option because of performance reasons.

Then use template.
Actually in my program A is an array of numerical values, but in some
circumstances it is preferable to treat certain ranges of an array
separately. For this purpose I made a slice (Aref) class that references
an array and simply allows to index a range of elements in it, e.g. it
is possible to "name" elements 10..20 of an array and index them from
0..10.

There were no problems until I tried to make slices writeable.

Any suggestions for implementing this?

The STL chose to create the const_iterator trait.
IMO Alf P. Steinbach's suggestion is the one you need.

Now if you want settle for a runtime solution you can use a boolean to
know if you can modify the reference or not.

class Aref {
A &ref;
bool is_const;

public:
Aref(A &a) : ref(a),is_const(false) { }
explicit Aref(const A &a) : ref(const_cast<A&>(a)),is_const(true) {}

int read() const { return ref.read(); }
void set(int val)
{
if(this->is_const)
throw std::runtime_error("access violation");
ref.set(val);
}
};

Then
A a1(1);
Aref ar1(a1);
ar1.set(0);//OK

const A a2(2);
Aref ar2(a2);
ar2.set(0);//Throw

Michael
 
M

Michael DOUBEZ

SzH a écrit :
Why does C++ forbid making constructors const? Is this going to be
included in the next version of the standard?

I found this (dates 1995):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1995/N0798.htm
Why was the proposal rejected?

What is basically proposed is to restrict the kind of cv-instanciation
depending on the cv of the parameter. This means (in the case of the
wrapper class) that the storage into the class in not const thus forcing
a cons_cast of the parameter in the const case; relying on the constness
of the wrapper class.

But, doing so, you have no constness check wihtin the wrapper class.
a method:
// taking the example of the OP
void set(int val)const
{
this->ref.set(val); //this is legal although parameter is const
}

This can lead to nasty bug.

The solution would be to have a keyword specifying the member inherits
its constness from the constness of the class (the inverse of mutable)
and have:

class Aref{
elbatum A& ref;

public:
Aref(A& a):ref(a){}//ref is not const
const Aref(A&):ref(a){}//ref is const
private:
Aref(const A& a);
//....
};

Michael
 
M

Michael DOUBEZ

Michael DOUBEZ a écrit :
SzH a écrit :

What is basically proposed is to restrict the kind of cv-instanciation
depending on the cv of the parameter. This means (in the case of the
wrapper class) that the storage into the class in not const thus forcing
a cons_cast of the parameter in the const case; relying on the constness
of the wrapper class.

But, doing so, you have no constness check wihtin the wrapper class.
a method:
// taking the example of the OP
void set(int val)const
{
this->ref.set(val); //this is legal although parameter is const

Oups! This is not legal of course with a reference.
Let's make it a pointer
this->ref->set(val);

Michael
 
Ad

Advertisements

M

Michael DOUBEZ

Michael DOUBEZ a écrit :
Oups! This is not legal of course with a reference.
Let's make it a pointer
this->ref->set(val);

Gosh! It is legal.
this->ref.set(val); //this is legal although parameter is const

Sorry for polluting the group.

Michael
 

Top