Inheritance: providing a & as arg derivative for a function,

Consider this code example:

#include <iostream> class base { public: base() { std::cout << "base constructed" << std::endl; } base(const base & source) { std::cout << "base copy-constructed" << std::endl; } }; class derived : public base { public: derived() { std::cout << "derived constructed" << std::endl; } derived(const derived &) = delete; derived(const base & source) : base(source) { std::cout << "derived copy-constructed from base" << std::endl; } }; int main() { derived a; base b(a); derived c(a); return 0; } 

Why is calling base::base(const base &) ok, but calling derived::derived(const base &) not? Both expect a base link, and both give a derived link. It is my understanding that the derivative "is the base."

Why does the compiler insist on using derived::derived(const derived &) if it has no problems using base::base(const base &) if it is provided with a reference to an object of type derived?

+4
source share
3 answers

Apparently, the “deletion” of one of the things by default does not affect the actual deletion. Some terrible disgusting remainder from the once proud default copy designer remained lying around to come up and tell you a formidable voice: "I deeeaaaaaad!".

This is not a completely surprising fact for me, although I did not know about it specifically before I asked this question. I could not quote you in the corresponding section of the standard (and I am sure that in the corresponding section there is no mention of ghouls, although this should). And I'm also sure that there is a reason why this happens when you follow an incredibly complicated story about some terrible event that would work terribly if it weren’t.

And, unfortunately, for you, if there is something lying around this better match than converting to a base class, this is what will be used. For example, in this code:

 #include <iostream> class A { }; class B { public: void foo(const A &) { ::std::cerr << "B::foo(const A &) called!\n"; } void foo(const B &) { ::std::cerr << "B::foo(const B &) called!\n"; } }; int main() { B b; A &ar = b; b.foo(b); b.foo(ar); }; 

will lead to this conclusion:

 B::foo(const B &) called! B::foo(const A &) called! 

This is exactly as you expected. The compiler does not view the situation as ambiguous. And if you made void foo(const B &) private or protected member, then the compiler will still match it in preference to another, and will tell you that you tried to access what had an access specifier that said you you can not.

Think that you need to “delete” something by simply declaring it as a special access specifier, which is even more limited than private.

+2
source
 derived a; derived c(a); 

You have obviously deleted the copy constructor. This means that the second line above will not compile. The difference with the base argument is that in the base case there is no base constructor declared that accepts derived& , therefore transformations are applied.

But in the case of derived there is such a function, it is declared and deleted. Overload resolution will find both derived(derived const &) and derived(base const &) , as both are declared, and will select the first as the best match. Then he will find that he is removed and complains.

If you want to use another constructor with the derived object, you need to explicitly specify:

 derived c( static_cast<base&>(a) ); 

In this case, the best overload becomes derived( base const& ) , and the code will be compiled.

+2
source

I am going to continue and say this because the signature is derived c(a); exactly matches the signature of the function you deleted. C ++ will try to avoid casting / promoting / converting, if possible, and will prefer the closest match. Casting a to the appropriate type ( base ) should fix it so that it ceases to coordinate the signature of the remote function and begins to match the signature of another constructor.

0
source

Source: https://habr.com/ru/post/1439596/


All Articles