在尝试编写shared_ptr
的包装器时会隐藏用户的内存分配和释放,同时支持继承类,我偶然发现了一些非常奇怪的错误,表明编译器在重载期间查找错误的函数,或者我对混合重载和模板的了解是错误的。所以我写了这个东西用于测试:
#include <iostream>
void out(int i) {
std::cout << i << '\n';
}
template <class T>
struct Inst {
template <class TT>
Inst(const TT &) {out(1);}
Inst(const Inst &) {out(2);}
template <class TT>
Inst(TT &&) {out(3);}
Inst(Inst &&) {out(4);}
Inst() {out(-1);}
~Inst() {out(1000);}
};
class K {};
class KK : K {};
int main() {
out(3000);
K k; KK kk; Inst<K> i;
Inst<K> I1{k};
Inst<K> I2{kk};
Inst<K> I3{i};
Inst<K> I4{K()};
Inst<K> I5{KK()};
Inst<K> I6{Inst<K>()};
out(2000);
}
我理所期望的是I1
和I2
撰写1
,I3
撰写2
,I4
和{{1}写I5
和3
写I6
,至少另外两个对象在不同点写4
。然而,当使用-1
使用gcc 4.8.2编译时,我的机器跳过了一个对象,并为每个其他非自动构造函数写了-std=c++11
。我做错了什么?
答案 0 :(得分:6)
TT&&
中的Inst(TT &&) {out(3);}
有点特别。所以,特别是甚至有一个特殊的“术语”被称为universal reference
。
简而言之,TT&&
不您的想法。这里有两件事情可以发挥作用:参考折叠和模板演绎
由于TT
是一个模板参数,并且您在&&
前面加了T&&
,这就是Inst<K> I1{k}; ---> Inst(K&)
Inst<K> I2{kk}; ---> Inst(KK&)
Inst<K> I3{i}; ---> Inst(Inst<K>&)
Inst<K> I4{K()}; ---> Inst(K&&)
Inst<K> I5{KK()} ---> Inst(KK&&)
在您的示例中的含义:
TT&&
3
成为完全匹配会发生什么,并且是您所做的所有调用的选定构造函数,这就是为什么每个调用都会看到i
的原因(除了I6
和{{1}})。
如需进一步阅读,请参阅: