Conflict between copy constructor and forwarding constructor

前端 未结 3 465
面向向阳花
面向向阳花 2020-12-01 19:03

This problem is based on code that works for me on GCC-4.6 but not for another user with CLang-3.0, both in C++0x mode.

template 
struct My         


        
3条回答
  •  难免孤独
    2020-12-01 19:34

    I'm just in the bar with Richard Corden and between us we concluded that the problem has nothing to do with variadic or rvalues. The implicitly generated copy construct in this case takes a MyBase const& as argument. The templated constructor deduced the argument type as MyBase&. This is a better match which is called although it isn't a copy constructor.

    The example code I used for testing is this:

    #include 
    #include i
    
    template 
    struct MyBase
    {
        template  MyBase(S&&... args):
            m(std::forward(args)...)
        {
        }
        T m;
    };
    
    struct Derived: MyBase >
    {
    };
    
    int main()
    {
        std::vector                vec(3, 1);
        MyBase > const fv1{ vec };
        MyBase >       fv2{ fv1 };
        MyBase >       fv3{ fv2 }; // ERROR!
    
        Derived d0;
        Derived d1(d0);
    }
    

    I needed to remove the use of initializer lists because this isn't supported by clang, yet. This example compiles except for the initialization of fv3 which fails: the copy constructor synthesized for MyBase takes a MyBase const& and thus passing fv2 calls the variadic constructor forwarding the object to the base class.

    I may have misunderstood the question but based on d0 and d1 it seems that both a default constructor and a copy constructor is synthesized. However, this is with pretty up to date versions of gcc and clang. That is, it doesn't explain why no copy constructor is synthesized because there is one synthesized.

    To emphasize that this problem has nothing to do with variadic argument lists or rvalues: the following code shows the problem that the templated constructor is called although it looks as if a copy constructor is called and copy constructors are never templates. This is actually somewhat surprising behavior which I was definitely unaware of:

    #include 
    struct MyBase
    {
        MyBase() {}
        template  MyBase(T&) { std::cout << "template\n"; }
    };
    
    int main()
    {
        MyBase f0;
        MyBase f1(const_cast(f0));
        MyBase f2(f0);
    }
    

    As a result, adding a variadic constructor as in the question to a class which doesn't have any other constructors changes the behavior copy constructors work! Personally, I think this is rather unfortunate. This effectively means that the class MyBase needs to be augmented with copy and move constructors as well:

        MyBase(MyBase const&) = default;
        MyBase(MyBase&) = default;
        MyBase(MyBase&&) = default;
    

    Unfortunately, this doesn't seem to work with gcc: it complains about the defaulted copy constructors (it claims the defaulted copy constructor taking a non-const reference can't be defined in the class definition). Clang accepts this code without any complaints. Using a definition of the copy constructor taking a non-const reference works with both gcc and clang:

    template  MyBase::MyBase(MyBase&) = default;
    

提交回复
热议问题