About const combined with reference and virtual inheritance - c++

I have the following code:
class A
{
int x;
public:
A(int i=25) { x=i; }
int& f() const { return x; }
};
int main()
{
A a(15);
cout<<a.f();
return 0;
}
I get an error saying that I can't initialize a reference of type int& from an expression of type const int? If I modify function's f() header, like "int f() const" or like "int& f()" it works. I know that a const function can't modify the members of the class, but in my example that's not happening and still is something wrong I can't understand.
Secondly, I have this piece of code:
class B { /* instructions */ };
class D1: virtual B { /* instructions */ };
class D2: virtual B { /* instructions */ };
class D3: B { /* instructions */ };
class D4: private B { /* instructions */ };
class D5: virtual public B { /* instructions */ };
class M1: D1, public D2, D3, private D4, virtual D5
{ /* instructions */ };
class M2: D1, D2, virtual D3, virtual D4, virtual D5
{ /* instructions */ };
The question is how many times is B inherited in M1 and and in M2. I think the answer is 3 (first from D1, then from D3 and then from D4) for both, but I'm not sure. Am I right?

You're returning a reference to a member of your class. However, since you method is const, you're not allowed to return a non-const reference, because this would allow for the end user to modify your class state, after calling a const method.
Consider this:
class A
{
int x;
public:
A(int i=25) { x=i; }
const int& f() const { return x; }
};
int main()
{
A a(15);
int &ref = const_cast<int &>(a.f());
ref = 42;
// a.x == 42
return 0;
}
Using const_cast is generally a bad idea, it's here to demonstrate what could happen if you were allowed to return a non-const reference from a const method.
The end user could change the property x from your class, because of a call to a const function, which is not supposed to happen.
Returning the result by value is imo the way to go, since it's just an integer.

First, your function may not be modifying a const value, but you're trying to return a const value as a non-const, which could then be modified. The compiler is protecting you. To keep it const, modify it as:
const int &f() const {... }
or
int f() const { .... }
The first returns a const reference, the second returns the result by-value.

In a non-static const member function, the type of this is changed to A const *const. Therefore it follows that the type of x is changed to int const&. A const object cannot be implicitly-converted to a non-const version of its type.

For your first question:
class A
{
int x;
public:
A(int i=25) { x=i; }
const int& f() const { return x; }
//^^You should add const here
};
int main()
{
A a(15);
cout<<a.f();
return 0;
}
Otherwise, you are returning non-const reference from a const member function. Another option is to simply return by value.

const int& f() const is the way.
You're right, 3 times for both.

Related

A delicate issue about const qualifier when objects are referenced by other objects

Imagine that I have an object A that refers to an object B through its address. The address to B is given in the constructor of A.
Some methods of A modify B, other do not.
Here is a very simple illustration :
class B {
public :
void f1() const;
void f2();
};
class A {
public :
A(B * pb) {m_pb=pb;}
void g1() {m_pb->f1();} // g1 do not modify *m_pb
void g2() {m_pb->f2();} // g2 do modify *m_pb
private :
B * m_pb;
}
Now, imagine that in some part of my code, I have a variable whose type is const B &. I want to create an object A to call the g1 method that do not modify the object B. But I cannot construct an object A at all.
One solution would be to create 2 classes A1 and A2 with A1 referencing a const B * and defining only method g1 while A2 would reference a B * and would define both g1 and g2. (g1 would be defined in 2 different places). I don't find this solution very elegant.
I wonder whether there is a way to explain to the compilator that :
const A won't modify the object B that it is referring.So a const A objet can be constructed using a const B object.
I wonder whether there is a way to explain to the compilator that : const A won't modify the object B that it is referring.So a const A objet can be constructed using a const B object.
What you want is const constructor and there isn't one. But you can avoid duplicating the interface with inheritance:
struct B{
void f1() const{}
void f2() {}
};
class constA
{
public:
constA(const B* b):b(b){}
// Should be const as it does not changes `constA` object.
void g1() const {b->f1();}
// No harm in this, but can be protected if you want
const B* getB() const { return b;}
private:
const B* b;
};
class A : public constA
{
public:
A(B* b):constA(b){}
void g2() const { getMutableB()->f2();}
private:
B* getMutableB() const { return const_cast<B*>(getB());}
};
int main()
{
B b;
const B cb;
A a(&b);
a.g2();
a.g1();
constA ca(&cb);
ca.g1();
//ca.g2();
constA ca2(&b);
ca.g1();
//ca.g2();
}
EDIT: (Per request from #formerlyknownas_463035818 with which I agree ) Quick refresher on const_cast:
int main()
{
const int x = 5;
int y = 5;
const int* c_ptr = &x;
//'ptr' is valid object with known value.
int* ptr = const_cast<int*>(c_ptr);
// Is valid because '*ptr' is not modified
int value = *ptr;
// Undefined behaviour because '*ptr' is const object.
*ptr = 5;
const int* c_ptr2 = &y;
//'ptr2' is valid object with known value.
int* ptr2 = const_cast<int*>(c_ptr2);
// Is valid because '*ptr2' is not modified
int value = *ptr2;
// Is valid because '*ptr2' is not a const object.
*ptr2 = 5;
}
Given that A only has non-const B constructor then getB() always returns a pointer to a object that is not const. So the second part of the above example applies and everything is safe.
Note that const casting const B in order to pass it to A would produce UB when calling g2 but that is true for all B* ptrs with unknown origin and there's nothing A class can do about it.

Pass a C++ member function to a C function

We have a structure that accepts C function pointers:
int one(int x)
{
}
int two(int x)
{
}
struct Cstruct
{
int (*fn1)(int);
int (*fn2)(int);
};
Now I have a C++ class that has below methods:
class A
{
public:
int one(int x)
{
}
int two(int x)
{
}
int three(int x)
{
struct Cstruct cstr = {&this->one, &this->two};
}
};
While trying to initialize class A methods address to a instance of Cstruct compiler is giving error of an invalid conversion?
How can I assign the Class member function address to Cstruct?
You cannot do it, because C++ pointer to a non-static member function is not compatible with a non-member function pointer type. This is because member functions require an additional argument - the object on which the member function needs to be called, which becomes this pointer inside the invocation.
If you make your member functions static, your code would compile. However, it would not necessarily do what you want to achieve, because one and two have no access to other non-static members of A.
A trick to passing member functions to C functions requires passing an additional void* pointer with the "registration" record, and having C code pass it back to your static callback functions:
struct Cstruct
{
void *context; // Add this field
int (*fn1)(void*, int);
int (*fn2)(void*, int);
};
class A
{
public:
static int oneWrap(void* ptr, int x)
{
return static_cast<A*>(ptr)->one(x);
}
static int twoWrap(void* ptr, int x)
{
return static_cast<A*>(ptr)->two(x);
}
int one(int x)
{
}
int two(int x)
{
}
int three(int x)
{
struct Cstruct cstr = {this, &this->oneWrap, &this->twoWrap};
}
};
C code would need to pass the value of context to fn1 and fn2:
cs.fn1(cs.context, 123);
cs.fn2(cs.context, 456);

Is there a better/safe way to cast non-const reference of shared_ptr to a base class?

If you have a class Base with virtual methods and a class Implementation which implements the virtual methods, is there any way to cast std::shared_ptr < Implementation > & to std::shared < Base > &? The compiler allows this for const references, but for non const references it fails as in "Case A" in the code below. Is there an easy way to do this?
If not, how safe is my workaround "questionable_cast" in Case B?
#include <iostream>
#include <memory>
class Base
{
public:
virtual void set_value(int x) = 0;
};
class Implementation : public Base
{
public:
Implementation() : m_value(0) { }
void set_value(int x) override
{
m_value = x;
}
int get_value() const
{
return m_value;
}
private:
int m_value;
};
void do_something(std::shared_ptr<Base>& base)
{
base->set_value(5);
/// Code like this makes the non-const argument necessary
base = std::make_shared<Implementation>();
}
template <class T, class U>
std::shared_ptr<T>& questionable_cast(std::shared_ptr<U>& u)
{
/// This code is here to assure the cast is allowed
std::shared_ptr<T> tmp = u;
(void)tmp;
return *reinterpret_cast<std::shared_ptr<T>*>(&u);
}
int main()
{
std::shared_ptr<Implementation> a = std::make_shared<Implementation>();
// The following line causes a compiler error:
// invalid initialization of reference of type ‘std::shared_ptr<Base>&’ ...
// do_something(a);
// do_something(std::dynamic_pointer_cast<Base>(a));
// This is the workaround
do_something(questionable_cast<Base>(a));
std::cerr << "a = " << a->get_value() << std::endl;
return 0;
}
Two obvious solutions to the problem as originally asked: 1. Make do_something take a const reference to a shared_ptr (or a shared_ptr by value). 2. Create a named shared_ptr and pass a reference to that: Eg
int main()
{
std::shared_ptr<Implementation> a = std::make_shared<Implementation>();
std::shared_ptr<Base> b = a; // This conversion works.
do_something(b); // Pass a reference to b instead.
return 0;
}
Your questionable_cast function is a violation of the strict aliasing rules, and invokes undefined behaviour. It's quite likely to work in initial tests, and then a new release of the compiler will crank up the optimization a notch, and it will fail during a demo.
To handle the case where do_something changes the pointer:
int main()
{
std::shared_ptr<Implementation> a = std::make_shared<Implementation>();
std::shared_ptr<Base> b = a; // This conversion works.
do_something(b); // Pass a reference to b instead.
const auto aa = std::dynamic_pointer_cast<Implementation>(b);
if (aa)
a = aa;
else
; // Handle the error here
return 0;
}
If do_something guarantees to return a pointer of the same derived type, even if it doesn't return the same pointer, wrap it in a template function:
template <typename T>
void do_something_ex( std::shared_ptr<T>& a )
{
std::shared_ptr<Base> b = a;
do_something(b)
a = std::dynamic_pointer_cast<T>(b);
if (!a)
throw_or_assert;
}

Initialize static member in initialization list

How do I make G::t static? (e.g., G::t should be of type const static int&) G::t is defined by the constructor parameter p and not available anywhere else.
class G {
public:
// I want to make this static.
const int& t;
G(const int& p);
};
G::G(const int& p) : t(p) {}
int main() {
int a=2;
const int& b = a;
G g = G(b);
}
e.g.:
const int a = 10;
class foo
{
static const constexpr int& t = a;
};
You can't initialize static member in constructor, because constructors are for objects, not for class as whole.
Why are you even using a constructor if you want to set static members to be used by static functions? That's not what constructors are for. You definitely can't initialize a static member in an initializer-list, because that would mean it got initialized every time you constructed another object. (Just think about the difference between per-class static data and per-instance data and it should be obvious why the question makes no sense).
You could use a static local, which is initialized on first use so can be initialized at run-time, and then can be accessed by the static member functions:
class G {
public:
static int t(const int& p = 0, bool set = false);
G(const int& p);
};
G::G(const int& p) { t(p, true); }
int G::t(const int& p, bool set)
{
static bool is_set = false;
if (!is_set)
{
if (!set)
throw std::logic_error("Cannot use G::t before it is set");
}
else if (set)
throw std::logic_error("Cannot set G::t more than once");
static const int tt = p;
set = true;
return tt;
}
int main() {
int a=2;
const int& b = a;
G g = G(b);
int t = G::t();
}
But this is a horrible hack, you should think carefully about your design and whether using a constructor is appropriate here, or even if using a class is appropriate.
The member initializer list can only be used to initialize instance variables, so if t is static, you can only reassign it in the constructor body.
And because references can't be reassigned, you have to use a pointer. A reference_wrapper doesn't work because it would need to be statically initialized (unless you have a global int that you can use for that).
E.g.
class G {
public:
static const int* t;
G(const int& p);
};
const int* G::t; // definition in .cpp
G::G(const int& p) {
t = &p;
}

friend function returning reference to private data member

I have two questions about the following code.
class cls{
int vi;
public:
cls(int v=37) { vi=v; }
friend int& f(cls);
};
int& f(cls c) { return c.vi; }
int main(){
const cls d(15);
f(d)=8;
cout<<f(d);
return 0;
}
Why does it compile, since f(d) = 8 attemps to modify a const object?
Why does it still print 15, even after removing the const attribute?
It is not modifying a const object as a copy of d is being made due to the argument of f() being passed by value and not by reference. This is also the reason that d is unchanged as it is not being modified.