Hello. There is a Visual C ++ 2015 and code:

#define SHOW_ME printf("%s\n", __FUNCSIG__) template <class T> struct wrapper { int data = 10; wrapper() = default; template <class U> wrapper(const wrapper<U>&) { SHOW_ME; } }; 

I am trying to provoke him as follows:

 wrapper<long> wp1; wrapper<long> wp2{ wp1 }; 

It does not work as expected. For some reason, the default copy constructor is called instead of mine. Addition:

 wrapper(const wrapper<T>&) = delete; 

does not save the situation. A compilation error, the compiler does not see the template constructor point-blank and asks for something more appropriate. If you remove the const qualifier, then everything works:

 template <class U> wrapper(wrapper<U>&) { SHOW_ME; } 

at random revealed the next priority overloads

 (wrapper<T>&) > (wrapper<U>&) > (const wrapper<T>&) > default_ctor > (const wrapper<U>&) 

While he refused const, but the problems did not end there. If you try to apply this scheme for operator = then we get the following:

 template <class U> wrapper<T>& operator=(wrapper<U>&) { SHOW_ME return *this; } ... wrapper<long> wp1; wrapper<long> wp2; wp1 = wp2; 

Again the default version is invoked, although the const is gone. Going over different options, I accidentally discovered that if I added a definition to a class

 wrapper<T>& operator=(const wrapper<T>&); 

That all works and the option with class U is caused but how ?! I just added a definition which is not used in the code. Am I somehow overwriting the default operator? But even if I add a body to this function, the compiler prefers the template one anyway, and without this definition, it refused to see it.

  1. Is there any intelligible explanation for all the logic of these overloads, or are these compiler bugs or am I doing something wrong?
  2. How to effectively protect classes from unauthorized copying?

    1 answer 1

    Meyers has a very similar situation in "Efficient and Modern C ++" (section 5.4). The bottom line is that the normal resolution of overloads begins.

    Having = delete does not mean that the constructor does not participate in resolving the overload, but simply that it has no body. Further, by passing the lvalue wp1 to the constructor, you can pass it either to the generated constructor or to your template one. The generated (or rather, the default constructor, it is not generated at all - you banned), the constructor turns out to be a more accurate match, while the addition of const is required for both it and the template - the result is clear. But if you remove the const from the template, then it turns out to correspond more appropriately (it does not require the addition of const ) and, accordingly, it is he who is called.

    Does that clarify the situation?

    If, for example, you enter (and disable) the default replicator without const ,

     wrapper(wrapper<T>&) = delete; 

    and at the same time pass the rvalue - there will be no problem:

     wrapper<long> wp2{std::move(wp1)};