复制构造函数和转发构造函数之间的冲突

CTM*_*ser 11 c++ constructor clang c++11

此问题基于在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)... ) {}
};
Run Code Online (Sandbox Code Playgroud)

一个对象MyBase可以采用任何构造函数参数列表,只要T支持那个构造签名.问题与特殊成员函数有关.

  1. IIUC,构造函数模板取消自动定义的默认构造函数.但是,由于模板可以接受零参数,因此它将充当显式定义的默认构造函数(只要是T可默认构造的).
  2. IIUC,确定类的复制构造策略忽略了构造函数模板.这意味着在这种情况下,MyBase将获得一个自动定义的复制构造函数(只要是T可复制的),即通道T复制构造.
  3. 也可以将上一步应用于移动构造.

因此,如果我传递一个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 };  // which constructor gets called
Run Code Online (Sandbox Code Playgroud)

我的用户的问题是将其用作基类.编译器抱怨他的类无法合成自动定义的复制构造函数,因为它找不到与基类的构造函数模板的匹配.它不应该MyBase为自己的自动拷贝构造函数调用自动拷贝构造函数吗?CLang错误地提出了冲突吗?

Die*_*ühl 10

我和理查德科登在酒吧里,我们之间得出结论,这个问题与变量或右值无关.在这种情况下,隐式生成的复制构造采用MyBase const&as参数.模板化构造函数推导出参数类型为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 }; // ERROR!

    Derived d0;
    Derived d1(d0);
}
Run Code Online (Sandbox Code Playgroud)

我需要删除初始化列表的使用,因为clang还不支持.这个例子编译除了初始化fv3失败之外:复制的复制构造函数MyBase<T>接受a MyBase<T> const&并因此传递fv2调用variadic构造函数将对象转发给基类.

我可能误解了这个问题,但基于d0并且d1似乎默认构造函数和复制构造函数都是合成的.但是,这是gcc和clang的最新版本.也就是说,它没有解释为什么没有合成复制构造函数,因为有一个合成.

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

#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);
}
Run Code Online (Sandbox Code Playgroud)

因此,将问题中的可变参数构造函数添加到没有任何其他构造函数的类中会更改行为复制构造函数的工作!就个人而言,我认为这是相当不幸的.这实际上意味着MyBase需要使用复制和移动构造函数来扩充类:

    MyBase(MyBase const&) = default;
    MyBase(MyBase&) = default;
    MyBase(MyBase&&) = default;
Run Code Online (Sandbox Code Playgroud)

不幸的是,这似乎不适用于gcc:它抱怨默认的复制构造函数(它声称默认的复制构造函数采用非const引用不能在类定义中定义).Clang接受此代码时没有任何投诉.使用非const引用的复制构造函数的定义适用于gcc和clang:

template <typename T> MyBase<T>::MyBase(MyBase<T>&) = default;
Run Code Online (Sandbox Code Playgroud)