4

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?

Earth Engine
  • 10,048
  • 5
  • 48
  • 78
  • You've brought this on yourself with the inclusion of the `static_assert`. I can think of no good reason to not allow conversion from type `T&` to type `const T&` - can you? – Chad Oct 25 '11 at 02:44
  • This is just a dummy implementation of my class. The template constructor should have different logic than the copy constructor. – Earth Engine Oct 25 '11 at 03:56
  • To implement the template convert constructor, usually a contained storage should be used to store the value of t. If t is instance of MyClass, it would introduce wrapping over wrapping, which is what I am tring to prevent. – Earth Engine Oct 25 '11 at 04:01
  • I understand why you ask now. The original code is misleading. I have updated the code. – Earth Engine Oct 25 '11 at 04:51

2 Answers2

1

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.
Yun Huang
  • 4,256
  • 7
  • 27
  • 36
  • obviously there are different implementations. If you remove both A and B in GCC, you will get a failed assert, but this is not happen in VC++. I refered the latest draft (N3242), it seems saying that a copy constructor is non-template(12.8.2). So at least in this case GCC seems to against it and allowed the template version to be copy constructor. – Earth Engine Oct 26 '11 at 00:09
  • I should say that the standard said "a member function template is NEVER INSTANTIATED to perform the copy of a class object to an object of its class type"(12.8.7). According to this, I consider both GCC and VC2010 is failed to follow. GCC instantiated the template version if both A and B removed; and VC2010 instantiated the template version when one of them removed but not all. – Earth Engine Oct 26 '11 at 00:22
  • 1
    for the class compare assert, the correct expression should be `!std::is_same::type>::type, MyClass>::value` – Earth Engine Oct 26 '11 at 02:11
  • I am going to mark this as the correct answer, just because it includes another case study for GCC. – Earth Engine Nov 22 '11 at 21:56
  • Yes, when using std::is_same, always remember to remove reference and constant modifiers. – Yun Huang Nov 24 '11 at 02:00
0

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?

A constructor that is not a copy constructor can still be used to copy objects. In your case the constructor instantiated from the constructor template is used for copying the object. Which is fine.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • This involves another problem. If both lines A and B are removed, Visual Studio 2010 generates a trivial copy constructor and none of construction of object b and c drop to the template, i.e. the compiler accepts the code happily, which is not what I want as well. – Earth Engine Oct 25 '11 at 23:57