C++ c++;11具有可变通用引用的构造函数和复制构造函数
如果我们有带有通用引用参数的构造函数,如何声明复制构造函数C++ c++;11具有可变通用引用的构造函数和复制构造函数,c++,templates,c++11,constructor,c++14,C++,Templates,C++11,Constructor,C++14,如果我们有带有通用引用参数的构造函数,如何声明复制构造函数 struct记录{ 模板 显式记录(参考&&…参考){ cout在您的示例中,转发引用与Record&一起使用 因此,您可以为记录&添加额外的重载(转发到复制构造函数): Record(Record&other):Record(static_cast(other)){ 或使用转发引用的sFiAe。 < P>这是一个很坏的做法,在转发引用上超载(见有效的现代C++,项目26)。它们往往会因为过载解决规则而吞没所有传递给它们的信息。 在
struct记录{
模板
显式记录(参考&&…参考){
cout在您的示例中,转发引用与Record&
一起使用
因此,您可以为记录&
添加额外的重载(转发到复制构造函数):
Record(Record&other):Record(static_cast(other)){
或使用转发引用的sFiAe。
< P>这是一个很坏的做法,在转发引用上超载(见有效的现代C++,项目26)。它们往往会因为过载解决规则而吞没所有传递给它们的信息。
在您的示例中,您正在用非常量记录
对象构造一个记录
对象,这就是您的复制ctor无法执行的原因
分派给复制构造函数,因为无法为记录实例化模板&
如果您发现自己经常这样做(不推荐),您可以通过定义一个类型特征来完成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;
模板
使用no\u copy\u ctor=typename std::enable\u if<
!std::is_same::value | | sizeof…(参考文献)>::type;
因此,将上述内容写成
template<class Ref1, class ...Refs, typename = no_copy_ctor<Record&, Ref1, Refs...>>
explicit Record(Ref1&& ref, Refs&&... refs)
{ /*...*/ }
模板
显式记录(参考1和参考,参考和…参考)
{ /*...*/ }
问题
调用Record rec2(rec);
时,有两个可行的构造函数:复制构造函数Record(Record const&)
和带有Refs={Record&}
的变量构造函数,其结果是Record(Record&)
。后者是一个更好的候选人,因为它是一个不太符合简历要求的推荐人,所以它会赢,即使这不是你想要的
解决方案
您希望删除任何应该调用移动或复制构造函数的内容,使其不再是可变构造函数的可行候选。简单地说,如果Refs…
由一个单一类型组成,该类型要么是对从记录派生的类型的引用,要么只是一个普通值-我们不希望使用可变构造函数tructor。包含派生实例也很重要,因为您肯定希望SpecialRecord sr;Record r(sr);
调用复制构造函数
既然出现了这种情况,将其作为类型特征是很有用的。基本情况是,它既不是复制也不是移动:
template <typename T, typename... Ts>
struct is_copy_or_move : std::false_type { };
模板
结构是_copy_或_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>>
{ }
模板
结构是复制还是移动
:std::是的基础吗
{ }
然后我们只需要用这个SFINAE的替代品替换我们的变量构造函数:
template <typename... Refs,
typename = std::enable_if_t<!is_copy_or_move<Record, Refs...>::value>
>
Record(Refs&&...);
模板
>
记录(参考文献和…);
现在,如果参数是这样的,那么这应该是对复制或移动构造函数的调用,变量构造函数将不再可行。我无法写出完整的答案,但这应该可以帮助您基本思想是使用SFINAE。@DanielJour这与我的p.p.S.部分有点类似,但thx,我很高兴知道这是已知的问题…对于fu来说为了验证这个问题,应该指出的是,通用引用将正式被称为转发引用。可以找到这方面的建议。是否有任何缺点(使用手动const_cast
)?那么move-ctr呢?@tower120:删除const
可能很危险,我在添加const
时看到的唯一副作用是使用其他重载(如此处所示)。@tower120:可能已经调用了move构造函数,但如果需要,您可以为const-Record&
添加额外的重载。“顺便说一句,您可能更喜欢使用static_cast来添加常量”-为什么?@tower120:请看,您必须(重新)使用SFINAE解决方案实现默认构造函数。事实上,情况更复杂,您忘记检查sizeof…(参考文献)==0
@Jarod42您假设OP想要使用变量构造函数作为零参数构造函数。@Jarod42在Record rec2(rec)
中Refs
的大小实际上是零。您能详细说明一下检查它吗?对于大小,我的意思是您也禁止Record rec2(rec,foo);
。
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)
{ /*...*/ }
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>>
{ }
template <typename... Refs,
typename = std::enable_if_t<!is_copy_or_move<Record, Refs...>::value>
>
Record(Refs&&...);