代码
#include <iostream>
using namespace std;
#define PF cout << __PRETTY_FUNCTION__ << endl;
class berlp {
public:
berlp() { }
void p() { }
};
template <typename T>
class derp {
public:
derp() = default;
derp(const T & a) : mem(a) {
a.p();
mem.p();
PF
}
template <typename U>
derp(U && a) : mem(std::forward<U>(a)) {
PF
}
T mem;
};
int main(int argc, const char * argv[])
{
berlp one;
derp<berlp &> f(one); // problems with this list below
derp<const berlp &> h(one); // problem with this follows
return 0;
}
使用XCode和CLang输出 这一切都编译得很好,这是输出......
derp<berlp &>::derp(const T &) [T = berlp &]
derp<const berlp &>::derp(U &&) [T = const berlp &, U = berlp &]
问题
derp<berlp &> f(one);
:derp构造函数中的a.p()应该失败,因为“a”是“const berlp&amp;”参考折叠后,p()不是常量。其次,用“a”(const berlp&amp;)初始化“mem”(berlp&amp;)不应该起作用。似乎“derp(const T&amp; a)”中的“const”只是没有做任何事情。最后,为什么它甚至使用第一个构造函数而不是模板化的构造函数,似乎它会在不破坏const的情况下完成所有这些?
derp<const berlp &> h(one);
:为什么这个调用使用模板化的构造函数,而另一个似乎正是我们追求的?这不是一个太糟糕的问题,因为它似乎没有破坏任何东西,但它确实允许你修改构造函数中传递的berlp,而另一个构造函数(据说)不应该。
所以,我要么非常困惑,要么出现了什么!请帮忙!
答案 0 :(得分:1)
这里确实存在多个问题:
为什么derp<berlp&>(one)
使用第一个构造函数?
构造函数的声明为derp(T const&)
,变为derp(berlp& const&)
并折叠为derp(berlp&)
,因为没有const
引用或引用引用。这在8.3.2 [dcl.ref]第6段中说明:
如果typedef(7.1.3),类型模板参数(14.3.1)或decltype-specifier(7.1.6.2)表示类型TR是对类型T的引用,则尝试创建类型“对cv TR的左值引用”创建类型“对T的左值引用”,而尝试创建类型“对cv TR的右值引用”则创建类型TR。
显然,将berlp&
传递给采用berlp&
的构造函数是完全匹配的,模板构造函数不能做得更好。因此,选择了非模板构造函数。
为什么用derp<berlp&>(one)
调用berlp
?
这里没有真正的惊喜:mem
的类型为berlp&
,并使用berlp&
进行初始化,因此非const
成员的工作正常。
使用derp<berlp const&>
并传递berlp&
时,模板构造函数是完美的匹配,显然是选择的。 berlp const&
类型的成员变量刚刚用berlp&
初始化,隐式转换为berlp const&
。这里也不足为奇。
我认为你对参考折叠规则感到有些困惑。将const
置于错误的位置也无济于事:将其置于右侧实际上应该清楚地显示大部分混淆,并且my preference的一部分将const
置于右侧