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

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

    template <typename Args...>
    MyBase( Args&& ...x ) : m( std::forward<Args>(x)... ) {}
};
MyBase对象可以接受构造函数参数的任何列表,只要T支持该构造签名即可。问题与特殊成员函数有关。
  • IIUC,构造函数模板会取消自动定义的默认构造函数。但是,由于模板可以接受零个参数,因此它将充当显式定义的默认构造函数(只要T是默认可构造的)。
  • IIUC,确定类的复制构造策略时会忽略构造器模板。这意味着在这种情况下,MyBase将获得一个自动定义的复制构造函数(只要T是可复制的),它将引导T复制构造。
  • 也将上一步应用于移动构造。

  • 因此,如果我将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
    

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

    最佳答案

    我只是和Richard Corden在一起,我们之间的结论是,问题与可变参数或右值无关。在这种情况下,隐式生成的拷贝构造将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 }; // ERROR!
    
        Derived d0;
        Derived d1(d0);
    }
    

    我需要删除对初始化程序列表的使用,因为clang尚不支持此方法。除了fv3的初始化失败之外,该示例均会编译:为MyBase<T>合成的拷贝构造函数采用MyBase<T> const&,因此传递fv2调用可变参数构造函数,将对象转发给基类。

    我可能误解了这个问题,但是基于d0d1,似乎默认构造函数和拷贝构造函数都是综合的。但是,这是最新版本的gcc和clang。也就是说,它没有解释为什么没有合成拷贝构造函数,因为有一个合成的拷贝构造函数。

    为了强调此问题与可变参数列表或右值无关:以下代码显示了调用模板化构造函数的问题,尽管看起来好像调用了拷贝构造函数,而拷贝构造函数从来都不是模板。这实际上是某种令人惊讶的行为,我绝对没有意识到:
    #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);
    }
    

    结果,将问题中的可变参数构造函数添加到没有任何其他构造函数的类中,将改变行为复制构造函数的工作方式!我个人认为这很不幸。这实际上意味着类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;
    

    关于c++ - 复制构造函数和转发构造函数之间的冲突,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9287250/

    10-11 22:36
    查看更多