I have a fstream & member in a class, on which I'm calling the seekg function in a const function of the class, and yet the code compiles. I checked, and the seekg is not declared const (nor should it be), so how is this happening?
This is my code:
class Test {
fstream &f;
public:
Test(fstream &f_): f(f_) {}
int fid() const {
f.seekg(5);
return 0;
}
};
It turns out the const does not apply to members that are pointers or references, as stated here.
The best explanation I've seen of this is here where it states that inside const member functions, this is a const T *, where T is the class.
In your example that means that all the const modifier on fid() does is to change this from a Test * to a const Test * inside the function. When you write f., this is accessed as this->f. which is of type fstream & const. The reference is const but what it refers to is not, so calling a function that modifies it causes no problems.
The rule is defined in [expr.ref]/4:
If E2 is declared to have type “reference to T”, then E1.E2 is an lvalue; the type of E1.E2 is T. [...]
In practice you should consider a reference to T, as a const pointer to T with automatic dereferencement. Internaly this is what are reference. And inside the standard, all rules that applies to reference (see [basic.life] for example) are those rules that would apply to a const pointer:
class Test {
fstream * const f;
public:
Test(fstream &f_): f(&f_) {}
int fid() const {
f->seekg(5);
return 0;
}
};
Related
dcl.type.cv provides an interesting example:
For another example,
struct X {
mutable int i;
int j;
};
struct Y {
X x;
Y();
};
const Y y;
y.x.i++; // well-formed: mutable member can be modified
y.x.j++; // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y); // cast away const-ness of y
p->x.i = 99; // well-formed: mutable member can be modified
p->x.j = 99; // undefined: modifies a const member
which indicates that, via const_cast, one may modify mutable members of a const qualified object, while you can't do that with non-mutable members.
To my understanding, this is because of the original constness of y itself. What would happen if we got rid of the mutable keyword, the const qualifier fot y, but modified the fields in a const method?
Example below:
#include <vector>
struct foo {
std::vector<int> vec{};
void bar() const {
auto& raw_ref = const_cast<std::vector<int>&>(vec);
raw_ref.push_back(0); // ok?
auto* raw_this = const_cast<foo*>(this);
raw_this->vec.push_back(0); // ok?
}
};
int main() {
foo f{};
f.bar();
}
Does it exhibit Undefined Behaviour? I would think that it does not, since we're modifying an originally non-const, but in a const context.
Additionally, notice that I provided two ways of modifying the vec. One with non-const reference and one with non-const pointer to this (which was originally const in this constext due to foo::bar being a const method). Do they differ in any particular way, given the question's context? I would assume that both are okay here.
Disclaimer: I am aware of mutable keyword, but this desing (not only being flawed) is simply an example. One could assume that the author of the code wanted to prohibit every single way of modifying vec except for push_backs.
Your quoted paragraph actually spelled out exactly what is undefined [dcl.type.cv]
Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.
A const reference/pointer to a non-const object doesn't make that object a const object, all your accesses are well-formed.
If I use mutable with const pointer like this:
class Test
{
public:
mutable const int* ptr; // OK
};
It's working fine.
But, If I use like this :
class Test
{
public:
mutable int * const ptr; // Error
};
An error :
prog.cpp:6:25: error: const 'ptr' cannot be declared 'mutable'
mutable int * const ptr;
^
prog.cpp: In function 'int main()':
prog.cpp:11:7: error: use of deleted function 'Test::Test()'
Test t;
^
prog.cpp:3:7: note: 'Test::Test()' is implicitly deleted because the default definition would be ill-formed:
class Test
^
prog.cpp:3:7: error: uninitialized const member in 'class Test'
prog.cpp:6:25: note: 'int* const Test::ptr' should be initialized
mutable int * const ptr;
Why does compiler give an error in second case?
const int * ptr;
The first is a pointer to constant data, which means you can change the pointer and where it points to, but you can't change the data it points to.
int * const ptr;
The second is a constant-pointer to non-constant data, which means you must initialize the pointer in your constructor(s) and then you can't make it point anywhere else. The data it points to can be modified though.
The mutable part in both cases applies to the pointer, the actual member variable, not the data it points to. And since the variable can't be both mutable and constant at the same time you should get an error message for that.
The 2nd case causes error because mutable and const can't be mixed; mutable can only be used with non-const data member.
applies to non-static class members of non-reference non-const type and specifies that the member does not affect the externally visible state of the class (as often used for mutexes, memo caches, lazy evaluation, and access instrumentation). mutable members of const class instances are modifiable.
BTW the following code causes the same error.
class Test
{
public:
mutable const int x; // Error;
mutable int const x; // ditto;
};
The 1st case is fine, because const int* is not a const pointer, but a pointer to const. That means it's fine to modify the pointer itself, and you can mark it mutable. (But you can't modify the pointee.)
BTW const pointer to const (e.g. mutable const int* const ptr;) causes the same error too.
struct Test
{
const int* ptr;
};
Translation: "The structure has a member. The member is a pointer. The pointer points to an integer which may not be mutated via the pointer."
The pointer itself may be mutated though, to point to a different const int.
It's probably simpler if we pick a non-reference type, so you can separate the types of the member (in your example a pointer), from the type of the pointed-to object.
struct Test1
{
int value;
};
Now, adding the mutable keyword to get
struct Test2
{
mutable int value;
};
just means we're allowed to mutate the member even when the structure itself is otherwise const.
In other words, all of this is OK in both cases:
Test1 a { 123 };
Test2 b { 123 };
// now mutate the object through a non-const reference
a.value = 42;
b.value = 42;
but this is different:
const Test1& ca = a;
ca.value = 69; // NO, a member of a const object is const
const Test2& cb = b;
cb.value = 69; // OK, a mutable member of a const object
So, now that we understand how mutable is being applied, the consider the problematic line:
mutable int * const ptr;
This is saying that ptr is both mutable (able to be mutated even when the object it's a member of is otherwise const) and const (not able to be mutated even when the object it's a member of is otherwise non-const).
The two are obviously contradictory.
The bugs.eclipse.org say's:
The mutable specifier can be applied only to names of class data
members (9.2) and cannot be applied to names declared const or
static, and cannot be applied to reference members.
Rule 7-1-1 (Required) A variable which is not modified shall be const qualified
If a variable does not need to be modified, then it shall be
declared with const qualification so that it cannot be modified. A
non-parametric variable will then require its initialization at the
point of declaration. Also, future maintenance cannot accidentally
modify the value.
void b ( int32_t * );
int32_t f ( int32_t * p1, // Non-compliant
int32_t * const p2, // Compliant
int32_t * const p3 ) // Compliant
{
*p1 = 10;
*p2 = 10;
b( p3 );
int32_t i = 0; // Non-compliant
return i;
}
The example included in the standard is focused on pointers. The rule requires all the pointers meeting the conditions to be const, e.g. int * const. If I understand it correctly, it does not require pointers and references to point to const objects, e.g. const int * or const int &. In fact, it is covered by another rule (but only for parameters!):
Rule 7-1-2 (Required) A pointer or reference parameter in a function shall be declared as pointer to const or reference to const if the corresponding object is not modified
So, does the rule 7-1-1 apply to references at all? A reference cannot be re-bound after it has been created, so it should be treated like a const pointer. Therefore all references should automatically comply with the rule 7-1-1.
EDIT (based on comments by Lightness Races in Orbit, Richard Critten & Peter and my experiments): Or does the rule apply to the type of referenced object in the case of references? I mean const int & vs. int & similarly to const int vs. int?
I am asking because my MISRA C++ checker keeps reporting violations on references… Example of its behavior:
class A
{
int property;
public:
A(int param) : property(param) {} // violation: should be: const int param
int get_property() const { return property; }
void set_property(int param) { property = param; } // violation: should be: const int param
};
class ConstA
{
const int property;
public:
ConstA(int param) : property(param) {} // violation: should be: const int param
int get_property() const { return property; }
// setter not allowed
};
void example1()
{
const A const_obj_A(1);
A nonconst_obj_A(2);
ConstA nonconst_obj_constA(3); // OK: used to create a non-const reference
const A& const_ref_A = nonconst_obj_A;
A& nonconst_ref_A = nonconst_obj_A; // OK: setter called
nonconst_ref_A.set_property(4);
ConstA& const_ref_constA = nonconst_obj_constA; // violation: no modification
// In fact, it seems to be impossible to make
// a non-violating ConstA& declaration.
// The only chance is to make another non-const reference
// but the last declaration in the chain will still violate.
}
void example2()
{
const A const_obj_A(1);
A nonconst_obj_A(2);
ConstA nonconst_obj_constA(3); // violation: used only in const reference
const A& const_ref_A = nonconst_obj_A;
A& nonconst_ref_A = nonconst_obj_A; // violation: no modification using the ref.
const ConstA& const_ref_constA = nonconst_obj_constA;
}
No, 7-1-1 does not apply to references.
A declaration of the form int32_t & const p2 is nonsense.
The only meaningful const qualification of a refeence is of the form const int32_t &p2 or the equivalent int32_t const &p2. Then need for these forms is covered completely in 7-1-2.
Misra 7-1-1 doesn't need to apply to such references because the philosophy of Misra is to specify constraints that the language standard does not, not to restate constraints already specified in the language (and enforced by compilers). Misra 7-1-1 requires a variable (say, of type int32_t) to be declared const if it will not be modified - such as i in the quoted example. In standard C++, it is not possible to create a non-const reference to that variable - unless a type conversion (aka "cast") is used to remove the constness. Misra rule 5-2-5 requires that such casts shall not be used.
There is significant semantic difference between C++ pointer and reference: reference is NOT an object (variable).
It's just object alias. So by strict formal meaning 7-1-1 shall not be applied to references.
Are all of the below declarations the same? If so, what is the standard way to declare a constant function?
const SparseMatrix transpose();
SparseMatrix transpose() const;
const SparseMatrix transpose() const;
The const on the left of the function name means the object that is returned cannot be modified. The const on the right means the method is apart of a class and does not modify any of its data members. Unless or course any of its data members are declared with the mutable keyword, in which case modification thereof is permitted despite a const guard.
The placement of the const keyword is unimportant when the return type of the function is of non-pointer type:
T const f(); // same as const T f();
However, note that the placement of the const keyword matters when using a pointer as the return type. For example:
const T* f();
This method returns a pointer to a const T. That is, what it points to is immutable. So you cannot do an assignment through a dereference of the returned pointer:
T* x = f();
*x = y; // error: assignment of read-only location '*(const T*)x'
When const is placed on the immediate right of the return type (that is a pointer), it means the pointer is const and cannot be changed.
T* const f();
int main()
{
T* x const;
x = f(); // error: assignment of read-only variable 'x'
}
Furthermore, if we have const on both sides of a pointer return type, and have const denoting "no modification of class members", then it's read as the following:
const T* const f() const;
A const member function named f that returns a const pointer to a const T
The first one will return a SparseMatrix that is const and cant be changed.
The second one declares a function that returns a SparseMatrix and assures the function will not change any class variables (assuming it is a member function, otherwise it wouldnt make sense with this deceleration) except for mutable members.
The final one does both.
1) return a const value
2) const function, no member changes inside it
3) 1)+2)
Was tryin out the stackeroverflow qn so it got me thinking why not overload the the function and I came up with a slightly different code but it says the function cannot be overloaded. My question is why? or is there a another way?
#include <iostream>
using std::cout;
class Test {
public:
Test(){ }
int foo (const int) const;
int foo (int );
};
int main ()
{
Test obj;
Test const obj1;
int variable=0;
do{
obj.foo(3); // Call the const function
obj.foo(variable); // Want to make it call the non const function
variable++;
usleep (2000000);
}while(1);
}
int Test::foo(int a)
{
cout<<"NON CONST"<<std::endl;
a++;
return a;
}
int Test::foo (const int a) const
{
cout<<"CONST"<<std::endl;
return a;
}
You can't overload based only on the constness of a non pointer, non reference type.
Think for instance if you were the compiler.
Faced with the line:
cout <<obj.foo(3);
which function would you call?
As you are passing by value the value gets copied either way. The const on the argument is only relevant to the function definition.
§13.1 where the Standard discusses about declarations that cannot be overloaded states -
Parameter declarations that differ
only in the presence or absence of
const and/or volatile are equivalent.
That is, the const and volatile
type-specifiers for each parameter
type are ignored [...]
Only the const and volatile
type-specifiers at the outermost level
of the parameter type specification
are ignored in this fashion; const and
volatile type-specifiers buried within
a parameter type specification are
significant and can be used to
distinguish overloaded function
declarations. [...]
when determining which function is
being declared, defined, or called.
"In particular, for any type T,
“pointer to T,” “pointer to const T,”
and “pointer to volatile T” are
considered distinct parameter types,
as are “reference to T,” “reference to
const T,” and “reference to volatile
T.”
EDIT 2:
As the post is essentially the same as the reffered post, except that the overloaded functions are now class member functions, I am trying to illustrate an additional aspect that could be useful to illustrate the concept of overloading which is not the same as overloading based on the 'constness' of the arguments (either in class scope or namespace scope). However the OP wanted to know how to differentiate the two overloads.
A way to overload them successfully relies on the cv qualification of the implied first parameter in case of member function calls as shown. The 'const' member function can only be called when the object expression used to invoke the overloaded member function is also a const. When a non const object expression is used to invoke the overloaded member function call, the non const version is preferred as it is an exact match (the call to const member function overload will require cv qualification of the first implied argument)
#include <iostream>
using std::cout;
class Test {
public:
Test(){}
int foo (const int) const;
int foo (int );
};
int main ()
{
Test obj;
Test const objc; // const object
obj.foo(3); // calls non const overload, object expression obj is non const
objc.foo(3); // calls const overload, object expression objc is const
}
int Test::foo(int a)
{
a++;
return a;
}
int Test::foo (const int a) const
{
return a;
}
As the answer to the other question explains, the two foos do not differ because they have equivalent parameter definitions.