带有可变通用引用和复制构造函数的c++11构造函数
c++11 constructor with variadic universal references and copy constructor
如何声明复制构造函数,如果我们有通用引用参数的构造函数,也?
http://coliru.stacked-crooked.com/a/4e0355d60297db57struct Record{
template<class ...Refs>
explicit Record(Refs&&... refs){
cout << "param ctr" << endl;
}
Record(const Record& other){ // never called
cout << "copy ctr" << endl;
}
Record(Record&& other){ // never called
cout << "move ctr" << endl;
}
};
int main() {
Record rec("Hello");
Record rec2(rec); // do "param ctr"
return 0;
}
根据std::tuple
http://en.cppreference.com/w/cpp/utility/tuple/tuple[查看案例3和8]的构造函数列表,这个问题在标准库中以某种方式解决了…但是我不能通过stl的代码。
注:关于c++构造函数通用引用和返回值优化(rvo)的问题
P.P.S.现在,我只是为真正的EXPLICIT调用添加了额外的第一个参数Record(call_constructor, Refs&&... refs)
。我可以手动检测如果我们只有一个参数,如果它是Record
,然后重定向调用复制ctr/参数ctr,但....我不敢相信这没有标准的方法……
在您的示例中,转发引用与Record&
一起使用。
所以你可以为Record&
添加一个额外的重载(转发到复制构造函数):
Record(Record& other) : Record(static_cast<const Record&>(other)) {}
问题
当你调用Record rec2(rec);
时,你有两个可行的构造函数:复制构造函数Record(Record const&)
和带Refs = {Record&}
的可变构造函数Record(Record&)
。后者是一个更好的候选,因为它是一个不那么cv限定的引用,所以即使这不是你想要的,它也会胜出。
您希望删除应该调用move或copy构造函数的任何内容,使其不再是可变构造函数的可行候选。简单地说,如果Refs...
由一个类型组成,该类型要么是对Record
派生类型的引用,要么只是一个普通值——我们不想使用可变的构造函数。包含派生的情况也很重要,因为您肯定希望SpecialRecord sr; Record r(sr);
调用复制构造函数…
,那么将它作为类型trait是很有用的。基本情况是它既不是复制也不是移动:
template <typename T, typename... Ts>
struct is_copy_or_move : std::false_type { };
我们只需要专门化一个类型:
template <typename T, typename U>
struct is_copy_or_move<T, U>
: std::is_base_of<T, std::decay_t<U>>
{ }
然后我们只需要用SFINAE的替代方法替换可变构造函数:
template <typename... Refs,
typename = std::enable_if_t<!is_copy_or_move<Record, Refs...>::value>
>
Record(Refs&&...);
现在,如果参数使得应该是对复制或移动构造函数的调用,则可变构造函数将不再可行。 重载转发引用是一种不好的做法(参见Effective modern c++ , Item 26)。由于重载解析规则,它们倾向于吞噬您传递给它们的所有内容。
在您的示例中,您正在从非const Record
对象构造Record
对象,这就是为什么您的复制函数没有执行。如果你这样命名
Record rec2(const_cast<Record const&>(rec));
则按预期工作。
一个解决方案是在构造函数上使用转发引用和禁用,如果应该调用复制函数;在可变变量的情况下,它会变得有些难看:
template <
class Ref1, class ...Refs,
typename = typename std::enable_if <
!std::is_same<Ref1, Record&>::value || sizeof...(Refs)
>::type
>
explicit Record(Ref1&& ref, Refs&&... refs)
{
cout << "param ctr" << endl;
}
现在调用
Record rec2(rec); // calls copy ctor
分派给复制构造函数,因为模板不能为Record&
实例化
如果你发现自己经常这样做(不推荐),你可以通过定义一个类型trait来完成SFINAE
来消除一些混乱。template<class T1, class T2, class... Refs>
using no_copy_ctor = typename std::enable_if <
!std::is_same<T1, T2>::value || sizeof...(Refs)>::type;
因此将上面的内容写成
template<class Ref1, class ...Refs, typename = no_copy_ctor<Record&, Ref1, Refs...>>
explicit Record(Ref1&& ref, Refs&&... refs)
{ /*...*/ }
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 使用复制构造函数复制双精度数组
- C 无可行的构造函数复制类型的变量
- 没有可行的构造函数复制类型 'MyString' 的数组元素
- 编译时,复制构造函数/复制分配和正常功能调用优化之间是否存在任何区别
- 如何最小化调用列表构造函数(复制构造函数)的次数?
- C 11矢量构造函数复制与范围
- 我定义了一个非复制构造函数;复制构造函数还会被隐式定义吗
- 可以将构造函数复制为转换运算符
- 将基类指针的构造函数复制到子类
- C++树类:构造函数/复制/内存泄漏
- 如何制作这个在模板构造函数复制中使用类型定义的类型的模板
- 将构造函数复制为模板化的成员函数
- 绕过私有复制构造函数/复制赋值C++
- C++通过构造函数复制对象
- 复制构造函数 - 复制C++中的对象
- 将带unique_ptr的类的构造函数复制到作为成员的抽象类