为什么我的 T& 和 T&& copy 构造函数模棱两可?
Why are my T& and T&& copy constructors ambiguous?
#include <iostream>
using namespace std;
class Myclass{
private:
int i;
public:
template<typename U>Myclass(U& lvalue):i(lvalue){cout<<i <<" template light reference" <<endl;i++;}
//Myclass(Myclass &lvalue):i(lvalue){cout<<i <<" light reference" <<endl;i++;}
template<typename U>Myclass(U&& rvalue):i(rvalue){cout<<i <<" template right reference" <<endl;i++;}
};
int main(int argc,char*argv[])
{
Myclass a(0);
Myclass b(a);
Myclass c(2);
return 0;
}
错误信息:
rightvalue.cpp: In function ‘int main(int, char**)’:
rightvalue.cpp:15:12: error: call of overloaded ‘Myclass(Myclass&)’ is ambiguous
rightvalue.cpp:15:12: note: candidates are:
rightvalue.cpp:10:23: note: Myclass::Myclass(U&&) [with U = Myclass&]
rightvalue.cpp:8:23: note: Myclass::Myclass(U&) [with U = Myclass]
rightvalue.cpp:4:7: note: constexpr Myclass::Myclass(const Myclass&)
rightvalue.cpp:4:7: note: constexpr Myclass::Myclass(Myclass&&) <near match>
rightvalue.cpp:4:7: note: no known conversion for argument 1 from ‘Myclass’ to ‘Myclass&&’
发生的情况是这样的(或者说,应该发生):为了解析这个构造函数调用
Myclass b(a);
编译器必须执行重载解析,并首先决定哪些构造函数是可行的候选函数。
首先要注意的是两个构造函数都是可行的:像T&&
这样的形式而不是总是解析为右值引用(只有在传递的是右值时才会出现这种情况)。这就是Scott Meyers所说的"通用引用"(注意,这个术语不是标准的)。
当编译器尝试执行类型推导以查看第二个构造函数是否可行时,在这种情况下,类型T
将被推导为Myclass&
-因为您传递的(a
)是左值;由于引用折叠规则,Myclass& &&
给出了Myclass&
,所以你最终得到了与第一个构造函数相同的签名。
调用是有歧义的吗?正如Marc Glisse在这个问题的评论中指出的那样,Jonathan Wakely在这个答案的评论中指出,no,它不应该是(正如这个答案的原始版本所声称的那样-我的过失)。
原因是标准中的一个特殊规则指定接受左值引用的重载比接受右值引用的重载更专门化。c++ 11标准第14.8.2.4/9段:
如果,对于给定的类型,两个方向的演绎都成功(即,转换后的类型是相同的)P和A都是引用类型(在被引用的类型替换之前)上图):
- 如果实参模板的类型是左值引用,则为形参模板的类型如果不是,则认为实参类型比另一个更专门化;否则,[…]
这意味着编译器有一个bug (bug报告的链接由Marc Glisse在问题的评论中提供)。
要解决这个错误,并确保接受T&&
的构造函数模板仅在传递右值时才会被GCC选中,您可以这样重写:
#include <type_traits>
template<typename U,
typename std::enable_if<
!std::is_reference<U>::value
>::type* = nullptr>
Myclass(U&& rvalue):i(rvalue)
{cout<<i <<" template right reference" <<endl;i++;}
我添加了一个sfinae约束,使编译器在传递左值时从重载集中丢弃此构造函数。
当传递左值时,实际上,对于某些X
(您传递的表达式类型为Myclass
), T
将被推导为X&
,而T&&
将解析为X&
;另一方面,当传递右值时,T
将从某个X
(您传递的表达式的类型,在您的例子中是Myclass
)推断为X
, T&&
将解析为X&&
。
由于SFINAE约束检查T
是否没有被推断为引用类型,否则会创建替换失败,因此保证仅当参数是右值表达式时才考虑构造函数。
总结一下:
#include <iostream>
#include <type_traits>
class Myclass
{
int i;
public:
template<typename U>
Myclass(U& lvalue):i(lvalue)
{
std::cout << i <<" template light reference" << std::endl;
i++;
}
template<typename U,
typename std::enable_if<
!std::is_reference<U>::value
>::type* = nullptr>
Myclass(U&& rvalue):i(rvalue)
{
std::cout << i <<" template right reference" << std::endl;
i++;
}
};
int main(int argc,char*argv[])
{
Myclass a(0);
int x = 42;
Myclass b(x);
Myclass c(2);
}
下面是一个的实例
- "error: no matching function for call to"构造函数错误
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 选择要调用的构造函数
- 如何委托派生类使用其父构造函数?
- 构造函数正在调用一个使用当前类类型的函数
- 没有用于初始化C++中的变量模板的匹配构造函数
- 初始化具有非默认构造函数的std::数组项的更好方法
- 当从函数参数中的临时值调用复制构造函数时
- 在c++构造函数中使用随机字符串生成器
- 一对向量构造函数:初始值设定项列表与显式构造
- 从构造函数抛出异常时如何克服内存泄漏
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 为什么此C++代码中的构造函数模棱两可以及如何修复它
- 将一组类转换为类模板并避免构造函数模棱两可
- C 汇编错误:对构造函数的引用是模棱两可的
- 为什么我的 T& 和 T&& copy 构造函数模棱两可?