If a class has a const reference data member that happens to change outside the scope of such class, is this undefined behaviour?
As an example, let's consider the following C++ code:
#include <iostream>
class A {
int x;
public:
A(int x): x(x){}
void change(int y){
x = y;
}
friend std::ostream & operator << (std::ostream & os, const A & a){
os << a.x;
return os;
}
};
class B {
const A & a;
public:
B(const A & a) : a(a) {}
friend std::ostream & operator << (std::ostream & os, const B & b){
os << b.a;
return os;
}
};
int main(){
A a(1);
B b(a);
std::cout << a << std::endl;
std::cout << b << std::endl;
a.change(2);
std::cout << a << std::endl;
std::cout << b << std::endl;
}
My compiler was able to execute it correctly and the debugger indicated that the x of B::a was changed.
Thank you for you help!
It is not undefined behavior. The const reference that is a member of B only means that an instance of B may not change it via that reference. Because it is a reference, however, something else may change it -- including other members of B that have their own non-const reference to the same instance of A.
Compare the addition of the member c to your existing B class, and note that we are changing it successfully within B::changeA() via the non-const reference and also from C::change() down in main():
#include <iostream>
class A {
int x;
public:
A(int x): x(x){}
void change(int y){
x = y;
}
friend std::ostream & operator << (std::ostream & os, const A & a){
os << a.x;
return os;
}
};
class C
{
A& a;
public:
C(A& a) : a{a} {}
void change(int y) { a.change(y); }
};
class B {
const A & a;
C& c;
public:
B(const A & a, C& c) : a(a), c{c} {}
friend std::ostream & operator << (std::ostream & os, const B & b){
os << b.a;
return os;
}
void changeA(int y) { c.change(y); }
};
int main(){
A a(1);
C c(a);
B b(a,c);
std::cout << a << ' ' << b << '\n';
a.change(2);
std::cout << a << ' ' << b << '\n';
b.changeA(3);
std::cout << a << ' ' << b << '\n';
c.change(4);
std::cout << a << ' ' << b << '\n';
}
See it run live on Coliru, which prints:
1 1
2 2
3 3
4 4
You may not change an object using a constant reference to it but you may change the object itself if it is not constant or using a non-constant reference to the object.
Consider the following demonstrative program.
#include <iostream>
int main()
{
int x = 10;
int &rx = x;
const int &crx = x;
std::cout << "rx = " << rx << '\n';
std::cout << "crx = " << crx << '\n';
rx = 20;
std::cout << "rx = " << rx << '\n';
std::cout << "crx = " << crx << '\n';
return 0;
}
Its output is
rx = 10
crx = 10
rx = 20
crx = 20
It is the same as using a pointer to constant data. For example
#include <iostream>
int main()
{
int x = 10;
int *px = &x;
const int *cpx = &x;
std::cout << "*px = " << *px << '\n';
std::cout << "*cpx = " << *cpx << '\n';
*px = 20;
std::cout << "*px = " << *px << '\n';
std::cout << "*cpx = " << *cpx << '\n';
return 0;
}
Related
This question already has answers here:
What is object slicing?
(18 answers)
Closed 1 year ago.
I am building up a CRTP interface and noticed some undefined behavior. So, I built up some sample code to narrow down the problem.
#include <iostream>
template <typename T>
class Base {
public:
int a() const { return static_cast<T const&>(*this).a_IMPL(); }
int b() const { return static_cast<T const&>(*this).b_IMPL(); }
int c() const { return static_cast<T const&>(*this).c_IMPL(); }
};
class A : public Base<A> {
public:
A(int a, int b, int c) : _a(a), _b(b), _c(c) {}
int a_IMPL() const { return _a; }
int b_IMPL() const { return _b; }
int c_IMPL() const { return _c; }
private:
int _a;
int _b;
int _c;
};
template <typename T>
void foo(const T& v) {
std::cout << "foo()" << std::endl;
std::cout << "a() = " << static_cast<Base<T>>(v).a() << std::endl;
std::cout << "b() = " << static_cast<Base<T>>(v).b() << std::endl;
std::cout << "c() = " << static_cast<Base<T>>(v).c() << std::endl;
}
int main() {
A v(10, 20, 30);
std::cout << "a() = " << v.a() << std::endl;
std::cout << "b() = " << v.b() << std::endl;
std::cout << "c() = " << v.c() << std::endl;
foo(v);
return 0;
}
The output of this code is:
a() = 10
b() = 20
c() = 30
foo()
a() = 134217855
b() = 0
c() = -917692416
It appears that there is some problem when casting the child class, which implements the CRTP "interface", to the interface itself. This doesn't make sense to me because the class A plainly inherits from Base so, shouldn't I be able to cast an instance of A into Base?
Thanks!
You copy and slice when you cast to Base<T>.
Cast to a const Base<T>& instead:
std::cout << "a() = " << static_cast<const Base<T>&>(v).a() << std::endl;
std::cout << "b() = " << static_cast<const Base<T>&>(v).b() << std::endl;
std::cout << "c() = " << static_cast<const Base<T>&>(v).c() << std::endl;
It turns out I was casting incorrectly to a value rather than a reference
std::cout << "a() = " << static_cast<Base<T>>(v).a() << std::endl;
should become
std::cout << "a() = " << static_cast<const Base<T>&>(v).a() << std::endl;
I wonder if this code is well defined. Seems to me fine, but hopefully someone can explain this (either correct, or wrong) in standard terms.
template <typename T>
class B {
public:
B (int n) : p {n} {}
T& operator++ () {
++p;
return static_cast<T&> (*this); // (X)
}
int p;
};
class Z : public B<Z> {
public:
using B::B;
int boo = 42;
};
and usage:
Z z {10};
std::cout << z.p << " " << z.boo << "\n";
Z& x = ++z;
std::cout << x.p << " " << x.boo << "\n";
x.boo = 1;
++x;
std::cout << z.p << " " << z.boo << "\n";
What people always do is to have a reference to the base class and pass the derived object to it. Here I have the opposite behavior in (X). Output of usage is as expected. I am asking if extension of Z can cause a problem?
I work actually to understand the concept of template and implement a simple one. I manage to almost cases execpt one to code a constructor to copy an instance of same class.
#include <iostream>
// template <typename T>
template <class T>
class vec2 {
private:
static int instance;
T const x;
T const y;
public:
vec2() : x(0), y(0) {
std::cout << "Default constructor" << std::endl;
vec2<T>::instance++;
return;
}
vec2(T const &x, T const &y) : x(x), y(y) {
std::cout << "Parametric constructor" << std::endl;
vec2<T>::instance++;
return;
}
vec2(vec2<T> const & src) {
*this = src;
std::cout << "Copy constructor" << std::endl;
vec2<T>::instance++;
return;
}
~vec2(){
std::cout << "Destructor" << std::endl;
vec2<T>::instance--;
return;
}
vec2 & operator=(vec2 const & rhs) {
this->x = rhs.get_x();
this->y = rhs.get_y();
return *this;
}
// get
static int get_instance() {
return vec2<T>::instance;
}
T get_x() const {
return this->x;
}
T get_y() const {
return this->y;
}
};
template <class T>
std::ostream & operator<<(std::ostream & out, vec2<T> const & rhs) {
out << "[ " << rhs.get_x() << ", " << rhs.get_y() << " ]";
return out;
}
template <class T>
int vec2<T>::instance = 0;
int main() {
vec2<float> a;
vec2<int> b(21, 42);
vec2<float> c(21.21f, 42.42f);
vec2<bool> d(true, false);
vec2<int> e(b);
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
std::cout << d << std::endl;
std::cout << e << std::endl;
std::cout << "a.get_x(): " << a.get_x() << std::endl;
std::cout << "a.get_y(): " << a.get_y() << std::endl;
std::cout << "b.get_x(): " << b.get_x() << std::endl;
std::cout << "b.get_y(): " << b.get_y() << std::endl;
std::cout << "c.get_x(): " << c.get_x() << std::endl;
std::cout << "c.get_y(): " << c.get_y() << std::endl;
std::cout << "d.get_x(): " << d.get_x() << std::endl;
std::cout << "d.get_y(): " << d.get_y() << std::endl;
return (0);
}
here the error message, but I'm not expert te read it, and i don't understand what i must change in my code. So if anybody I have an idea to help a newbee in C++, that's can be awesome.
clang++ -std=c++11 -Wconversion *.cpp && ./a.out
main.cpp:24:2: error: constructor for 'vec2<int>' must explicitly initialize the
const member 'x'
vec2(vec2<T> const & src) {
^
main.cpp:72:12: note: in instantiation of member function 'vec2<int>::vec2'
requested here
vec2<int> e(b);
^
main.cpp:9:10: note: declared here
T const x;
^
main.cpp:24:2: error: constructor for 'vec2<int>' must explicitly initialize the
const member 'y'
vec2(vec2<T> const & src) {
^
main.cpp:10:10: note: declared here
T const y;
^
main.cpp:38:11: error: cannot assign to non-static data member 'x' with
const-qualified type 'const int'
this->x = rhs.get_x();
~~~~~~~ ^
main.cpp:25:9: note: in instantiation of member function 'vec2<int>::operator='
requested here
*this = src;
^
main.cpp:72:12: note: in instantiation of member function 'vec2<int>::vec2'
requested here
vec2<int> e(b);
^
main.cpp:9:10: note: non-static data member 'x' declared const here
T const x;
~~~~~~~~^
main.cpp:39:11: error: cannot assign to non-static data member 'y' with
const-qualified type 'const int'
this->y = rhs.get_y();
~~~~~~~ ^
main.cpp:10:10: note: non-static data member 'y' declared const here
T const y;
~~~~~~~~^
4 errors generated.
As #Sam Varshavchik said in the comments, the problem is that you don't initialize your const members in your copy-constructor. Here is the correct implementation:
vec2(vec2<T> const & src) : x(src.get_x()), y(src.get_y()) { //<-- initialization of const members
std::cout << "Copy constructor" << std::endl;
vec2<T>::instance++;
return;
}
Also, *this = src; does feel all kinds of wrong.
here my original class code where i make a copy with *this = src and the result work great. So the question why that's work with regular class and not with the template ?
Plus I imagine the behavior with the regular class, the adress stay the same.
And the method to init all member can be complicated if the is a lot members variables in the class ?
main.c
#include "Vec2.hpp"
#include <iostream>
int main() {
Vec2 a;
Vec2 b(21,42);
Vec2 c(a);
std::cout << a << std::endl;
std::cout << "c.get_x(): " << c.get_x() << std::endl;
std::cout << "c.get_y(): " << c.get_y() << std::endl;
std::cout << "b.get_x(): " << b.get_x() << std::endl;
std::cout << "b.get_y(): " << b.get_y() << std::endl;
std::cout << "a.get_x(): " << a.get_x() << std::endl;
std::cout << "a.get_y(): " << a.get_y() << std::endl;
a = b;
std::cout << "a.get_x(): " << a.get_x() << std::endl;
std::cout << "a.get_y(): " << a.get_y() << std::endl;
return 0;
}
Vec2.cpp
#ifndef VEC2_H
# define VEC2_H
#include <iostream>
class Vec2 {
public:
Vec2(); // canonical
Vec2(float const x, float const y);
Vec2(Vec2 const &); // canonical
~Vec2(); // canonical
Vec2 & operator=(Vec2 const & rhs); // canonical
static int get_instance();
float get_x() const ;
float get_y() const ;
private:
static int instance;
float x;
float y;
};
std::ostream & operator<< (std::ostream & out, Vec2 const & rhs);
#endif
vec2.hpp
#include "Vec2.hpp"
#include <iostream>
Vec2::Vec2() : x(0), y(0) {
std::cout << "Default constructor" << std::endl;
Vec2::instance++;
return;
}
Vec2::Vec2(float const x, float const y) : x(x), y(y) {
std::cout << "Parametric constructor" << std::endl;
Vec2::instance++;
return;
}
Vec2::Vec2(Vec2 const & src) {
*this = src;
std::cout << "Copy constructor" << std::endl;
Vec2::instance++;
return;
}
Vec2::~Vec2() {
std::cout << "Destructor" << std::endl;
Vec2::instance--;
return;
}
Vec2 & Vec2::operator=(Vec2 const & rhs) {
this->x = rhs.get_x();
this->y = rhs.get_y();
return *this;
}
int Vec2::get_instance(){
return Vec2::instance;
}
float Vec2::get_x() const {
return this->x;
}
float Vec2::get_y() const {
return this->y;
}
std::ostream & operator<< (std::ostream & out, Vec2 const & rhs) {
out << "[ " << rhs.get_x() << ", " << rhs.get_y() << " ]";
return out;
}
int Vec2::instance = 0;
I want to add indent during serialization of object. But since operator<< can only contains 2 parameters:
struct A {
int member;
};
ostream& operator<<(ostream& str, const A& a)
{
return str;
}
Now my solution is like this:
struct A {
int m1;
int m2;
};
void print(const A& a, const int indent)
{
cout << string(indent, '\t') << m1 << endl;
cout << string(indent + 1, '\t') << m2 << endl;
}
Is there any better method of adding extra parameters during object serialization?
You could make a tuple or pair and send it to a operator<<function
or you could also do something like
std::ostream& operator<<(std::ostream& os, const A& param)
{
auto width = os.width();
auto fill = os.fill();
os << std::setfill(fill) << std::right;
os << std::setw(width) << param.m1 << std::endl;
os << std::setw(width) << fill << param.m2 << std::endl;
return os;
}
int main()
{
struct A a{1,2};
std::cout.width(4);
std::cout.fill('\t');
std::cout << a << std::endl;
}
This question already has answers here:
How can I use cout << myclass
(5 answers)
Closed 6 years ago.
I don't know the term in programing of what I'm asking (this is hobby for me) and I'm trying to new things here. See my working escenario:
#include <iostream>
class Foo {
int _x;
public:
Foo () : _x(0) {}
Foo (int x) : _x(x) {}
int get () const { return _x; }
};
int main () {
Foo f1;
Foo f2(10);
std::cout << "Value: " << f1.get () << std::endl; // 0
std::cout << "Value: " << f2.get () << std::endl; // 10
return 0;
}
Is it possible to use either f1 or f2 like this:
std::cout << "Value: " << f2 << std::endl; // shows 10
updated with correct code:
#include <iostream>
class Foo {
int _x;
public:
Foo () : _x(0) {}
Foo (int x) : _x(x) {}
int get () const { return _x; }
friend std::ostream &operator<<(std::ostream &os, const Foo& f) {
return os << f.get ();
}
};
int main () {
Foo f1;
Foo f2(10);
std::cout << "Value: " << f1.get () << '\n'; // 0
std::cout << "Value: " << f2.get () << '\n'; // 10
std::cout << "Value: " << f1 << '\n'; // 0
return 0;
}
Yes, this is overloading stream insertion operator.
#include <iostream>
class Foo {
int _x;
public:
Foo () : _x(0) {}
Foo (int x) : _x(x) {}
int get () const { return _x; }
friend std::ostream& operator<< ( std::ostream& stream, const Foo& foo );
};
std::ostream& operator<< ( std::ostream& stream, const Foo& foo ) {
stream << foo._x;
return stream;
}
int main () {
Foo f1;
Foo f2(10);
std::cout << "Value: " << f1 << std::endl; // 0
std::cout << "Value: " << f2 << std::endl; // 10
return 0;
}