这个问题是基于在 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
是可复制的)它将 channelT
复制构造。 - 也将上一步应用于移动构造。
所以如果我传递 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
调用可变构造函数将对象转发给基类。
我可能误解了这个问题,但基于 d0
和 d1
似乎默认构造函数和复制构造函数都被合成了。但是,这是最新版本的 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
还需要使用复制和移动构造函数进行扩充:
MyBase(MyBase const&) = default;
MyBase(MyBase&) = default;
MyBase(MyBase&&) = default;
不幸的是,这似乎不适用于 gcc:它提示默认的复制构造函数(它声称不能在类定义中定义采用非常量引用的默认复制构造函数)。 Clang 毫无怨言地接受了这段代码。使用采用非 const 引用的复制构造函数的定义适用于 gcc 和 clang:
template <typename T> MyBase<T>::MyBase(MyBase<T>&) = default;
关于c++ - 复制构造函数和转发构造函数之间的冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9287250/