suppose we have this as our setup:
#include <iostream>
class Base {
private:
int a;
public:
Base(int a)
: a(a) {
}
virtual void print() = 0;
};
class Child : public Base {
public:
using Base::Base;
void print() override {
//some code
}
};
class Wrapper {
private:
void* base_ptr = nullptr;
public:
Wrapper(void* base_ptr)
: base_ptr(base_ptr) {
std::cout << "void* version called\n";
}
Wrapper(Base* const& base_ptr)
: base_ptr(base_ptr) {
std::cout << "Base*const& version called\n";
}
};
if we do the following:
Child* child_ptr = new Child(1);
Wrapper w(child_ptr);
output is:
Base*const& version called
which is expected and completely normal.
but if we change Wrapper(Base *const&)
to Wrapper(Base*&)
we call the void*
version:
class Wrapper {
private:
void* base_ptr = nullptr;
public:
Wrapper(void* base_ptr)
: base_ptr(base_ptr) {
std::cout << "void* version called\n";
}
Wrapper(Base*& base_ptr)
: base_ptr(base_ptr) {
std::cout << "Base*const& version called\n";
}
};
int main() {
Child* child_ptr = new Child(1);
Wrapper w(child_ptr);
return 0;
}
for this version we get this output:
void* version called
but why is that? shouldn't Base*const&
just make it also compatible with temporary objects?
Ok. after sometime thinking about it, I figured it out. It's simply because there is an implicit cast happening where we try to call the constructor of second version, we change the type of Child*
to Base*
and because of that it's not anymore an LValue
and becomes an RValue
, therefore it can't bind to Base*&
and if we were about to change its signature to Base*&&
, it would choose this overload(which is expected because of the implicit cast happening there and making it an RValue
).