Search code examples
templatesvisual-c++constructorc++11gcc4

Multiple copy constructors specified


With Visual C++ 2010, I have a class like this:

class MyClass{
public:
    MyClass(){}
    MyClass(MyClass &){/*...*/}   //A
    MyClass(const MyClass &){/*...*/}   //B
    template<typename T> MyClass(T &&t){ static_assert(
        !std::is_same<typename 
        std::remove_cv<typename  std::remove_reference<T>::type>::type, 
        MyClass>::value,
        "Wrapping over wrapping is not allowed!"); } //C
};

int main(int, char**){
    MyClass a;
    const MyClass b(a); //assert fail if line A removed
    auto c=b; //assert fail if line B removed
}
//If both A and B exists
//Warning C4521: multiple copy constructors specified
//Furthermore, if both A and B removed
//No error or warnings; VC2010 accepts peacefully.
//In debug mode you will find the compiler generated trivial copy constructor

According to C++ standard, both line A and B are considered copy constructors, and C is a convert constructor. It is not surprising that I receive a warning that I declared multiple copy constructors. However, if I remove any of them, the static_assert fails and the code would not compile, that means the template constructor received control.

I am sure that this behaviour follows the rule of function overloading. However is this a conflict of two rules? If A and B are copy constructors and one of them was declared, any attempt to copy the objects should not drop to the template, is it right?

Update: According to N3242, 12.8.7,

"a member function template is NEVER INSTANTIATED to perform the copy of a class object to an object of its class type."

the correct implementation should be:

  • No assert failure should occur when either A or B or both removed.
  • If line B is removed, construction of c should fail because b is const.
  • If both lines are removed, compiler should generate a copy constructor for the class.
  • It is up to the implementation to warn the user if both lines exists.

Any comment?


Solution

  • First of all, template<T> should be template <typename T>.

    I am using gcc 4.4.3 on 64-bit Ubuntu Linux, the codes behave differently from what you have demonstrated in the post.

    • If nothing changed, the codes could be compiled without any warning. The constructor A and B are evoked one after another.
    • If I comment line A, it fails to be compiled just as what you said: failing in line const MyClass b(a);. The reason is that the object a is not constant, so constructor B can not be matched and the compiler has to instantiate the template constructor. Of course, const MyClass and MyClass are different types.
    • However, if I comment line B only, the codes could be compiled successfully and the template copy constructor was evoked. Object b is a constant object, so construct A can not be matched and the compiler instantiate the template constructor. However, the question remains: should the static_assert fails or not? The difference may be because of the platform/compiler difference. GCC seems implementing the is_same<MyClass&&, MyClass>::value to be true. You may use typeid to print out both of the types.