write to a const reference of ostream - c++

I am reading C++ primer. I encountered the following code:
#include <iostream>
#include <string>
using namespace std;
class PrintString {
public:
PrintString(ostream &o = cout, char c = ' '): os(o), sep(c) {}
void operator() (const string &s) const { os << s << sep; }
private:
ostream &os;
char sep;
};
int main() {
const PrintString printer;
printer("ABC");
return 0;
}
This code works, but I don't know why. Below is what I think, it would be great if anyone could help to point out where I am wrong...
Here, 'printer' is a const PrintString object, so its data members are const, so 'printer.os' is a const reference to cout. Therefore, we should not be able to write to 'printer.os' since writing to cout changes it.
Thanks in advance!

The reference isn't being modified, only what it refers to. This works the same with a pointer. If you had a pointer to int data member (int*), using it in a const member function would make its type int* const. You aren't able to change the pointer itself, but you can change what it points to. For example:
struct Foo
{
int a;
int* p = &a;
void foo() const
{
p = new int; // ERROR! Not allowed to modify const pointer
*p = 100; // OK, it's a pointer to a non-const int
}
};
So when using os, you're only modifying the object that it refers to, not the reference itself.

Your confusion with const-ness is better explained with a pointer rather than a reference.
Say you have:
struct A {int data;};
struct B
{
B(A* ptr) : aPtr(ptr) {}
A* aPtr;
};
int main()
{
A a1;
A a2;
const B b(&a1);
// This makes b a const object.
// This makes b.aPtr a const pointer. That means, you cannot change where it points to
// but you can still change the value of what it points to.
b.aPtr = &a2; // Not ok.
b.aPtr->data = 10; // OK.
}
The const-ness of b.aPtr is analogous to what you would see had you used a raw pointer.
int main()
{
A a1;
A a2;
A* const aPtr1 = &a1;
// This makes aPtr1 a const pointer. That means, you cannot change where it points to
// but you can still change the value of what it points to.
aPtr1 = &a2; // Not ok.
aPtr1->data = 10; // OK.
A const* aPtr2 = &a1;
// This makes aPtr2 a pointer to a const object. That means, you can change where it points to
// but you cannot change the value of what it points to.
aPtr2 = &a2; // ok.
aPtr2->data = 10; // Not ok.
}
When it comes to references, it is similar but with a small twist. There is no such thing as a non-const reference. A reference, once initialized, cannot reference another object.
A a1;
A& ref = a1; // ref cannot be changed to reference any other object.
In your case,
const PrintString printer;
has no effect on the const-ness of the member variable PrintString::os. It continues to reference a non-const ostream. This allows you to use:
const PrintString printer;
printer("ABC");

Try to think of the reference member as a const pointer member variable, and how constness would propagate to member variables when you declare a const object of such class. What gets const is the pointer itself, not what it points to. The same semantics happen with references, The thing is that references are already const (like a const pointer variable), so it doesn't change nothing for them.
#include <iostream>
struct S {
int *p;
int *const cp;
int &ref;
};
int main() {
using namespace std;
int i = 10;
const S s{&i, &i, i};
// s.p = &i; // can't do this, s.p gets const
*s.p = 20; // changing the pointee
cout << i << endl;
// s.p = &i; // can't do this, s.p was const already, and would get if it weren't
*s.cp = 30; // changing the pointee
cout << i << endl;
// s.ref ?; // can't make a reference refer to other object
s.ref = 40; // changing the pointee
cout << i << endl;
}

Related

Const class object with a pointer member

let's have an example program:
struct Example
{
int* pointer;
Example(int* p) : pointer(p) {}
};
int main()
{
int var = 30;
const Example object(&var);
object.pointer = &var; // error
*object.pointer = var;
}
I do know why the error appears - when creating a const Example object, pointer is actually const pointer, not a pointer to const. Therefore, when assigning &var to object.pointer it is not correct but assigning var to the *object.pointer works perfectly fine.
So is there any way to make this line:
*object.pointer = var;
not compile? I want the value that pointer points to to also be const. In other words, when creating a const object:
const Example object(&var);
I want constness to be applied also on the value itself, not only on the pointer:
const int* const pointer; // something like that
Either declare the data member like
const int *pointer;
or make it a protected or private data member. In the last case you can declare a non-constant member function that allows to change the value pointed to by the pointer. For a constant object of the class the function will not be called. For example
struct Example
{
private:
int* pointer;
public:
Example(int* p) : pointer(p) {}
int & get_value() { return *pointer; }
const int & get_value() const { return *pointer; }
};
const Example object(&var);
This creates a const object. Once a const object is created it cannot be changed. This is what a constant object means, by definition.
object.pointer = &var;
This attempts to change this object's member. This object cannot be changed. It is a constant object now, hence the error.
I want constness to be applied also on the value itself, not only on
the pointer:
const int* const pointer; // something like that
"Something like that" declares a pointer that's both a constant value itself, and it points to a constant value.
If you want an object with a pointer to const int, that would be:
const int *pointer;
And making the object itself constant has absolutely nothing to do with it, whatsoever. It only prevents modification of the object itself. Once you make something const it cannot be changed for any reason.
You can do something like that by
Making pointer an member function that return the pointer
Having non-const version of the function return a reference to the pointer to allow assignment to that
Having const version of the function return a copy of the pointer to prevent assignment
struct Example
{
private:
int* pointer_internal;
public:
Example(int* p) : pointer_internal(p) {}
int*& pointer() { return pointer_internal; }
int* pointer() const { return pointer_internal; }
};
int main()
{
int var = 30;
const Example object(&var);
object.pointer() = &var; // error: lvalue required as left operand of assignment
*object.pointer() = var;
}

Modifying element of const std::vector<T> via const_cast

Does the following program have undefined behavior?
#include <iostream>
#include <vector>
struct Foo
{
const std::vector<int> x;
};
int main()
{
std::vector<int> v = {1,2,3};
auto f = new Foo{v};
const_cast<int&>(f->x[1]) = 42; // Legal?
std::cout << f->x[1] << "\n";
}
Note that it not using const_cast to strip constness from f->x, but instead stripping constness from f->x[x], which presumably is represented by a separate array. Or is a translation allowed to presume that f->x[1] is immutable after it is created?
There is no Undefined Behavior in your example.
The above code does not invoke undefined behavior because the underlying data (int) is mutable. Let's look at a simpler example.
#include <iostream>
struct IntPtr {
int* data;
};
int main() {
int a = 0;
const IntPtr p { &a };
*p.data = 10;
std::cout << a; // Prints 10
}
All of this is perfectly legal to do because making IntPtr const results in data being a constant pointer to an int, NOT a pointer to a constant int. We can modify the data that p.data points to; we just can't modify p.data itself.
const IntPtr p { &a };
*p.data = 10; // This is legal
int b;
p.data = &b; // This is illegal (can't modify const member)
So how does this example apply to std::vector?
Let's add the ability to index into an IntPtr:
class IntPtr {
int* data;
public:
IntPtr() = default;
IntPtr(int size) : data(new int[size]) {}
// Even though this is a const function we can return a mutable reference
// because the stuff data points to is still mutable.
int& operator[](int index) const {
return data[index];
}
};
int main() {
const IntPtr i(100);
i[1] = 10; // This is fine
};
Even though the IntPtr is const, the underlying data is mutable because it's created by allocating an array of mutable ints. It's the same for std::vector: the underlying data is still mutable, so it's safe to const_cast it.

Constructor for `const` object

Consider this code:
#include <iostream>
class test
{
public:
test( char *arg )
: _arg( arg )
{}
char *_arg;
};
int main( )
{
char *txt1 = "Text one"; // Ignore this warning.
const char *txt2 = "Text two";
test t1( txt1 ); // Normal case, nothing new.
const test t2( txt2 ); // Since object is const, I'd like to be able to pass a const argument in.
}
It blows up with the error:
error: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]
const test t2( txt2 );
Then I tried to add a second constructor to test whether the compiler could select the right one for t2, my const object:
test( const char *arg )
: _arg( arg )
{}
but then the error is:
error: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]
: _arg( arg )
So declaring my object as const does not make its fields const as well.
How to properly declare my class constructors so that it can handle const and non-const object creation?
How to properly declare my class constructors so that it can handle const and non-const object creation?
The object isn't const during construction, or the constructor wouldn't be able to initialize the object (since all data members would be const).
So, constructors can't be const-qualified and you can't have a constructor overload used for const objects.
Now, you can overload on the argument, but your data member always has type char * during construction, although it's qualified to char * const (not const char *) when used in a const-qualified instance of test.
Options are:
overload constructor on argument type, and store a char * always. If you're passed a const char * you have to copy it (and you're responsible for knowing that you own and must deallocate this memory)
In this scheme, you rely on keeping the pointer private and using const-qualified accessors to stop the contents of the pointer being changed via a const-qualified object.
Again, you need to do this manually because char * const is a different type than const char *, because constness of the pointed-to type isn't related to constness of the pointer: having a const instance of your class just stops you mutating the pointer, not the characters it points to.
overload constructor and store a const char * always. This avoids copying but obviously doesn't work if you sometimes need to change the pointed-to characters
just write different mutable-string and immutable-string classes
If it helps, consider this:
template <typename T> struct test {
T* p_;
test(T *p) : p_(p) {}
};
template <typename T> test<T> mktest(T *p) { return {p}; }
and note that
const char *ccp = "immutable characters in a string literal";
char *cp = strdup(ccp);
auto a = mktest(ccp);
auto b = mktest(cp);
gives a the type test<const char>, and b the type test<char> and that these types are not the same, are not convertible, and are no more closely related in the language than to test<T> for any other type T.
A note: this is a long answer for a use case that might be a bad design. Yet, the reason and the main focus for the long answer is:
to show and explain what is not possible
to present a way in which one can make the compiler decide based on the constness of a parameter whether to create a holding const object and to preserve this information even if the object is passed on. Which is very close to the OP request.
As explained in other answers, you can have two constructors, one for const parameter and the other for non-const, but both constructors would just create an object that can be either const or non-const. So this doesn't help.
Another approach could be to have a factory method that would create either a const object or a non-const, according to the constness of the parameter. Though this may sound promising it would not allow to preserve the semantics of the difference between the two, as shown in the following pseudo code:
// creating an object holding a const, using the factory method `create`:
const MyClass const_holder = create(const_param);
// however you cannot prevent this:
MyClass non_const_holder = create(const_param);
The factory would create in the second case above a const object that would be copied or moved (or just created directly as non_const_obj with copy elision, since C++17 as mandatory copy elision). You cannot do anything to avoid the second case unless you delete copy and move, in which case the first case wouldn't work also and the all thing collapses.
So, without creating actually two different types it is impossible to preserve the information of which constructor was used and to avoid assignment of an object created with const param into an object that doesn't.
However, there is no real need to bother the user with the fact that there are two classes, with a proper template implementation the user can have simple code that gives the feeling that there is only one class in the game, something like:
// creating a const holder with a factory method
// the type would *remember* that it is holding a const resource
// and can act accordingly
auto const_holder = create(const_param);
// and you can also create a non const holder with the same factory
auto non_const_holder = create(param);
These operations would be allowed:
// (a) calling a non const method on the content of the non const holder
non_const_holder->non_const_method();
// (b) assigning a non const holder into a const holder object
// -- same as assigning char* to const char*
const_holder = non_const_holder;
These operations would NOT be allowed:
// (a) calling a non const method on the content of a const holder
const_holder->non_const_method(); // should be compilation error
// (b) assigning a const holder into a non const holder object
// -- same as one cannot assign const char* to char*
non_const_holder = const_holder; // should be compilation error
In a way, this is very similar to the idea of propagate_const...
The code would have a factory method:
template<class T>
Pointer<T> create(T* t) {
return Pointer<T>::create(t);
}
And two implementations for the template class Pointer.
base template:
template<class T>
class Pointer {
T* ptr;
Pointer(T* p) : ptr(p) {}
friend class Pointer<const T>;
public:
// factory method
static Pointer create(T* p) {
return p;
}
operator T*() { return ptr; }
operator const T*() const { return ptr; }
};
and a specialized one for the const version:
template<class T>
class Pointer<const T> {
const T* ptr;
Pointer(const T* p) : ptr(p) {}
public:
Pointer(const Pointer<T>& other) {
ptr = other.ptr;
}
// factory method
static const Pointer create(const T* p) {
return p;
}
operator const T*() { return ptr; }
operator const T*() const { return ptr; }
};
The main would look like:
int main() {
char str[] = "hello";
const char* const_str = "hello";
// non-const - good!
auto s1 = create(str);
// const holding non-const - ok!
const auto s2 = create(str);
// non-const that holds const - good!
auto s3 = create(const_str);
// also possible: const holding const
const auto s4 = create(const_str);
s1[4] = '!'; // OK
// s2[0] = '#'; // obviously doesn't compile - s2 is const
// s3[0] = '#'; // doesn't compile - which is good - s3 holds const!
// s4[0] = 'E'; // obviously doesn't compile - s4 is const
// avoids assignment of s3 that holds a const into s1 that holds a non-const
// s1 = s3; // <= doesn't compile - good!
s3 = s1; // allows assignment of `holding-non-const` into `holding-const`
s3 = s2; // allows assignment of `holding-non-const` into `holding-const`
s3 = s4; // allows assignment of `holding-const` into `holding-const`
}
Code: http://coliru.stacked-crooked.com/a/4729be904215e4b2
The problem you experience goes a bit deeper. It's an indication of a design issue.
You would like to expose only part of the API. You say that for a const object you will call only const methods and for non-const you can do anything. But this is problematic.
Either you accept const object strip it from const qualifier, and won't call non-const methods by a silent contract. Or, you need to limit methods, which makes a different object - type-wise.
C# library does it by providing a limited interface which wraps around original object and exposes only const methods. Something like this:
#include <iostream>
using namespace std;
struct A {
void f1() const {cout << "foo\n"; }
void f2() {cout << "bar\n"; }
};
struct readonlyA {
readonlyA(const A& a) : _a(a) {};
void f1() const {_a.f1();};
private:
const A& _a;
};
int main() {
A a;
readonlyA roA(a);
a.f2();
roA.f1();
roA.f2(); // error
return 0;
}
It's basically a read-only proxy.
The compiler does not care about what you actually do with the object at runtime.
Const works because the compiler will forbid certain things at compile time that could potentially change the object. This might be over-restrictive in certain situations, as the compiler often does not have the full picture of what's going on in the program.
Take for example the invocation a non-const member function on a const object: Even if the member function does not actually change the object's state, the compiler will still forbid it because the non-const function could potentially change the object.
Similar in your example: Even though you don't change the member for that particular const instance of the class, there could be other non-const instances of the same class somewhere, which is why it will refuse construct any instance of the class from a const object.
If you want a class that is guaranteed to leave its members unchanged, that would be a different type:
class test
{
public:
test( char *arg )
: _arg( arg )
{}
char *_arg;
};
class immutable_test
{
public:
immutable_test(char const* arg)
:_arg(arg)
{}
char const* _arg;
};
int main( )
{
char *txt1 = "Text one"; // Ignore this warning.
const char *txt2 = "Text two";
test t1( txt1 );
immutable_test t2( txt2 );
}
It can't be done. Just because the object is const, it doesn't mean that it guarantees the char* won't be used to modify its value. The constructor can mutate the argument, and code outside the class can modify its content if it is exposed. Consider the following example.
struct Test {
char* buffer;
Test(char* buffer):
buffer(buffer)
{
buffer[0] = 'a';
}
char* get() const {
return buffer;
}
};
int main(int argc, char* argv[]) {
std::string s{ "sample text" };
const Test t(s.data());
t.get()[1] = 'b';
t.buffer[2] = 'c';
std::cout << s << '\n';
}
The above prints abcple text, even though t is const. Even though it never modifies its members, it does change the pointed-to string and it allows outside code to modify it, too. This is why const objects cannot accept const char* arguments to initialize their char* members.
The compiler is preventing you from carelessly discarding the constness...
class test
{
public:
test(const char *arg)
: _arg(arg)
{}
const char *_arg;
};
Interesting.
Look at the example below (object myclass2) to learn how a const object does not necessarily provide the protection that you need!
#include <iostream>
#include <cctype>
#include <cassert>
class MyClass
{
public:
MyClass(char * str1, size_t size1) : str{str1}, size{size1}
{
}
char * capitalizeFirstChar()
{
*str = std::toupper(*str);
return str;
}
char nthChar(size_t n) const
{
assert(n < size);
return str[n];
}
char * str;
size_t size;
};
int main()
{
{
static char str1[] = "abc";
MyClass myclass1(str1, sizeof(str1) / sizeof(*str1));
myclass1.capitalizeFirstChar();
std::cout << myclass1.nthChar(0) << std::endl;
}
std::cout << "----------------------" << std::endl;
{
static const char str2[] = "abc";
// UGLY!!! const_cast
const MyClass myclass2(const_cast<char *>(str2), sizeof(str2) / sizeof(*str2));
// myclass2.capitalizeFirstChar(); // commented: will not compile
std::cout << myclass2.nthChar(0) << std::endl;
char c = 'x';
// myclass2.str = &c; // commented: will not compile
// The const myclass2, does not
// allow modification of it's members
myclass2.str[0] = 'Z'; // WILL COMPILE (!!) and should cause a segfault
// The const myclass2, CANNOT PROTECT THE OBJECT POINTED TO by str
// Reason: the member in MyClass is
// char *str
// not
// const char *str
std::cout << myclass2.nthChar(0) << std::endl;
}
}
Ok, the str member issue is actually best solved, by just making the members private.
But what about that ugly const_cast?
One way of solving this is splitting into a Const baseclass, and deriving for non-const behaviour (working with const-casts). Like this perhaps:
#include <iostream>
#include <cctype>
#include <cassert>
class MyClassConst
{
public:
MyClassConst(const char * str1, size_t size1) : str{str1}, size{size1}
{
}
char nthChar(size_t n) const
{
assert(n < size);
return str[n];
}
const char * str;
size_t size;
};
class MyClass : public MyClassConst
{
public:
MyClass(char * str1, size_t size1) : MyClassConst{const_cast<const char *>(str1), size1}
{
}
char * capitalizeFirstChar()
{
char * cp = const_cast<char *>(str);
*cp = std::toupper(*cp);
return cp;
}
};
int main()
{
{
static char str1[] = "abc";
MyClass myclass1(str1, sizeof(str1) / sizeof(*str1));
myclass1.capitalizeFirstChar();
std::cout << myclass1.nthChar(0) << std::endl;
}
std::cout << "----------------------" << std::endl;
{
static const char str2[] = "abc";
// NICE: no more const_cast
const MyClassConst myclass2(str2, sizeof(str2) / sizeof(*str2));
// a.capitalizeFirstChar(); // commented: will not compile
std::cout << myclass2.nthChar(0) << std::endl;
char c = 'x';
// myclass2.str = &c; // commented: will not compile
// The const myclass2, does not
// allow modification of it's members
// myclass2.str[0] = 'Z'; // commented: will not compile
std::cout << myclass2.nthChar(0) << std::endl;
}
}
Is it worth it? Depends.
One is kindof trading the const-cast outside of the class... for const-cast inside the class. So maby for library code (including ugly internals, and static code analysis exceptions), with clean outside usage, it's a match...

Returning a reference to an element of a class member container

Foo behaves like a circular iterator. Despite me being nervous about it, the code below compiles fine, but creates a run-time error. I receive the error even if I remove the consts from get_current(). Of course, I can return a pointer and it'll work; however, will I get better security returning a reference?
#include <iostream>
#include <array>
#include <memory>
class Foo
{
public:
Foo();
void next();
const int& get_current() const;
private:
std::array<std::unique_ptr<int>, 3> arr_;
unsigned i_;
};
Foo::Foo() : i_(0)
{
arr_[0] = std::unique_ptr<int>(new int(5));
arr_[1] = std::unique_ptr<int>(new int(6));
arr_[2] = std::unique_ptr<int>(new int(7));
}
void Foo::next()
{
++i_;
i_ %= 3;
}
const int& Foo::get_current() const
{
return *arr_[i_];
}
int main()
{
Foo foo;
int* p;
*p = foo.get_current();
//do something with p
std::cout << *p << std::endl;
foo.next();
*p = foo.get_current();
//do something with p
std::cout << *p << std::endl;
return 0;
}
int* p;
That's an uninitialised pointer, not pointing to anything. Dereferencing it gives undefined behaviour.
*p = foo.get_current();
That dereferences the invalid pointer. Boom!
Perhaps you want it to point to the array element
p = &foo.get_current();
or perhaps you want a copy of the array element
int n;
n = foo.get_current();
foo.get_current(); may well be returning a const reference, but after that you're attempting to take a value copy of that when assigning to *p.
Assigning to *p is what's causing you trouble as p is uninitialised. That's undefined behaviour and is manifesting itself in your case as a runtime error.
You could use code like const int& p = foo.get_current(); but do be aware that a reference can only be bound once, so you'll have to be careful with scoping.
Or, you could use std::shared_ptr<int> and make that the return type of get_current(), and strip your code entirely of bare pointers.
*p = ... You dereference int* P without having it properly initialized.
Change your code in main to
int p; // Remove *
p = foo.get_current();
//do something with p
std::cout << p << std::endl;
or if you really meant to use a pointer
const int* p;
p = &foo.get_current();
// ^ Take the address

Why can I change members of a const T& argument

If a function takes a const & argument it should be immutable.
Why can I change values in a class passed as const?
How can I prevent a function taking const & to change values?
Note: compiles on vs2012 ad g++ 4.8.2
#include "iostream"
class Foo {
public:
Foo() : a(-99) {}
int a;
};
class Bar{
public:
Bar (Foo& f): rFoo(f), pFoo(&f), foo(f) {}
Foo& rFoo;
Foo* pFoo;
Foo foo;
};
void setA (const Bar & b){
Foo* f = new Foo();
Foo f2 = *f;
f->a = 7;
//b.foo.a = 8; // error C3490: 'a' cannot be modified because it is being accessed through a const object
//b.pFoo = f; // error C3490: 'pFoo' cannot be modified because it is being accessed through a const object
b.pFoo->a = 9; // OK... the pointer is const the location its pointing to not...
b.rFoo.a = 10; // Maybe ... the reference may be const, the location its is referencing not ...
b.rFoo = *f; // Why can I modify the reference ?
}
int main(int argc, char* argv[]){
Foo f;
Bar b(f);
std::cout << "one: " << b.rFoo.a << std::endl;
b.pFoo->a = 1;
b.foo.a = 2;
std::cout << "two: " << b.rFoo.a << std::endl;
setA(b);
std::cout << "three: " << b.rFoo.a << std::endl;
return 0;
}
Many Thanks in advance
You can modify the reference for the same reason you can modify the pointed to object by pFoo. Since you aren't modifying the objects member, but another referenced object.
b.rFoo = *f; // Foo's assignment operator invoked
That's why you should not expose members variables. Since you can't enforce a prevention to modify referenced objects, other than in your own member functions.
b.rFoo = *f
is not changing rFoo itself, it is equivalent to
b.rFoo.operator=(*f);
which copies *f to the object referenced by rFoo.
The const qualifier of an object indicates that the abstract state of this object won't change (i.e., from the client's point of view). This is not exactly the same as saying that the object's raw bits are not going to change.
It is forbitten to C++ compilers to consider objects as raw bits, unless they can resolve the problem of aliasing. which in your case the compiler cannot. This is due to the fact that Foo* pFoo is a pointer to an object and consequently the state of this object is modifiable. That is, even if the object that pFoo points to changes, the object that contains pFoo (i.e., b) doesn't change since pFoo continues to point to the same object.