复制构造函数和转发构造函数之间的冲突 [英] Conflict between copy constructor and forwarding constructor

查看:171
本文介绍了复制构造函数和转发构造函数之间的冲突的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题是基于在GCC-4.6上适用于我的代码,但不是在C ++ 0x模式下的另一个使用CLang-3.0的用户。

  template< typename T> 
struct MyBase
{
// protected:
T m;

template< typename Args ...>
MyBase(Args& ... x):m(std :: forward< Args>(x)...){}
};

MyBase 的构造函数参数,只要 T 支持构造签名。问题与特殊成员函数有关。


  1. IIUC,构造函数模板取消自动定义的默认构造函数。但是,由于模板可以接受零参数,它将作为一个显式定义的默认构造函数(只要 T 是默认构造的)。

  2. IIUC,类的复制构造策略的确定忽略构造函数模板。这意味着在这种情况下, MyBase 将获得一个自动定义的复制构造函数(只要 T 是可复制的) c


所以如果我传递一个 MyBase< T> const& 作为唯一的构造函数参数,该构造函数被调用,转发一个还是隐式复制一个?

  typedef std :: vector< Int> int_vector; 
typedef MyBase< int_vector> VB_type;

int_vector a {1,3,5};
VB_type b {a};
VB_type c {b}; //其构造函数被调用

我的用户的问题是使用这个作为基类。编译器抱怨他的类不能合成一个自动定义的拷贝构造函数,因为它找不到与基类的构造函数模板的匹配。不应该是为它自己的自动复制构造函数调用 MyBase 自动复制构造函数?

解决方案

我刚刚在理查德·科德和我们之间的酒吧结论是这个问题与可变或右值无关。在这种情况下,隐式生成的复制结构需要一个 MyBase const& 作为参数。模板化的构造函数推导出参数类型为 MyBase& 。这是一个更好的匹配,虽然它不是一个复制构造函数。



我用于测试的示例代码是:

 #include< utility> 
#include< vector> i

template< typename T>
struct MyBase
{
template< typename ... S> MyBase(S& ... args):
m(std :: forward< S(args)...)
{
}
T m;
};

struct Derived:MyBase< std :: vector< int> >
{
};

int main()
{
std :: vector< int> vec(3,1);
MyBase< std :: vector< int> > const fv1 {vec};
MyBase< std :: vector< int> > fv2 {fv1};
MyBase< std :: vector< int> > fv3 {fv2}; //错误!

派生d0;
派生d1(d0);
}

我需要删除初始化列表的使用, ang,还。除了 fv3 的初始化,此示例编译失败:为 MyBase< T> 合成的复制构造函数采用 MyBase< T> const& ,因此传递 fv2 会调用将对象转发给基类的可变参数构造函数。



我可能误解了这个问题,但根据 d0 d1 ,似乎默认构造函数和复制构造函数。然而,这是与gcc和铛的漂亮的最新版本。也就是说,它不解释为什么没有复制构造函数被合成,因为有一个合成。



要强调这个问题与可变参数列表或右值无关:以下代码显示了调用模板化构造函数的问题,尽管它看起来好像复制构造函数被调用,并且复制构造函数从不是模板。这实际上是令人惊讶的行为,我绝对不知道:

  #include< iostream> 
struct MyBase
{
MyBase(){}
template< typename T> MyBase(T&){std :: cout < template\\\
; }
};

int main()
{
MyBase f0;
MyBase f1(const_cast< MyBase const&>(f0));
MyBase f2(f0);
}

结果,在问题中添加一个可变参数没有任何其他构造函数更改行为复制构造函数工作!我个人认为这是相当不幸的。这有效地意味着类 MyBase 需要增加copy和move构造函数:

  MyBase(MyBase const&)= default; 
MyBase(MyBase&)= default;
MyBase(MyBase&&)= default;不幸的是,这似乎不适用于gcc:它抱怨默认的复制构造函数(它声明默认的复制构造函数采用非const引用不能在类定义中定义)。 Clang接受这个代码没有任何投诉。使用具有非const引用的复制构造函数的定义与gcc和clang一起工作:

  template< typename T> MyBase T :: MyBase(MyBase< T>)= default; 


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 <typename T>
struct MyBase
{
//protected:
    T  m;

    template <typename Args...>
    MyBase( Args&& ...x ) : m( std::forward<Args>(x)... ) {}
};

An object of MyBase can take any list of constructor arguments, as long as T supports that construction signature. The problem has to do with the special-member functions.

  1. IIUC, the constructor template cancels the automatically-defined default constructor. However, since the template can accept zero arguments, it will act as an explicitly-defined default constructor (as long as T is default-constructible).
  2. IIUC, determination of a class' copy-construction policy ignores constructor templates. That means in this case that MyBase will gain an automatically-defined copy constructor (as long as T is copyable) that'll channel T copy-construction.
  3. Apply the previous step for move-construction too.

So if I pass a MyBase<T> const & as the sole constructor argument, which constructor gets called, the forwarding one or the implicit copying one?

typedef std::vector<Int>  int_vector;
typedef MyBase<int_vector>   VB_type;

int_vector  a{ 1, 3, 5 };
VB_type     b{ a };
VB_type     c{ b };  // which constructor gets called

My user's problem was using this in as a base class. The compiler complained that his class couldn't synthesize an automatically-defined copy constructor, because it couldn't find a match with the base class' constructor template. Shouldn't it be calling MyBase automatic copy-constructor for its own automatic copy-constructor? Is CLang in error for coming up with a conflict?

解决方案

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 <utility>
#include <vector>i

template <typename T>
struct MyBase
{
    template <typename... S> MyBase(S&&... args):
        m(std::forward<S>(args)...)
    {
    }
    T m;
};

struct Derived: MyBase<std::vector<int> >
{
};

int main()
{
    std::vector<int>                vec(3, 1);
    MyBase<std::vector<int> > const fv1{ vec };
    MyBase<std::vector<int> >       fv2{ fv1 };
    MyBase<std::vector<int> >       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<T> takes a MyBase<T> 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 <iostream>
struct MyBase
{
    MyBase() {}
    template <typename T> MyBase(T&) { std::cout << "template\n"; }
};

int main()
{
    MyBase f0;
    MyBase f1(const_cast<MyBase const&>(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 <typename T> MyBase<T>::MyBase(MyBase<T>&) = default;

这篇关于复制构造函数和转发构造函数之间的冲突的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆