代码
#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 &”,而 p() 不是 const。其次,用“a”(const berlp &)初始化“mem”(berlp &)应该不起作用。似乎“derp(const T & a)”中的“const”什么也没做。最后,为什么它甚至使用第一个构造函数而不是模板化的构造函数,它似乎可以在不破坏 const 的情况下完成所有这些?
derp<const berlp &> h(one);
:为什么这个调用使用模板构造函数,而另一个似乎正是我们所追求的?这不是一个太可怕的问题,因为它似乎没有破坏任何东西,但它确实允许您修改构造函数中传递的 berlp,而另一个构造函数(应该)不应该。
所以,我要么非常困惑,要么出事了!请帮忙!
最佳答案
这里真的有很多问题:
为什么
derp<berlp&>(one)
使用第一个构造函数?构造函数的声明是
derp(T const&)
变成derp(berlp& const&)
并折叠成derp(berlp&)
因为没有const
这样的东西引用或引用引用。这在 8.3.2 [dcl.ref] 第 6 段中说明:If a typedef (7.1.3), a type template-parameter (14.3.1), or a decltype-specifier (7.1.6.2) denotes a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR” creates the type TR.
显然,传递一个
berlp&
给构造函数使用berlp&
是完全匹配,模板构造函数不能做得更好。因此,选择了非模板构造器。为什么调用
derp<berlp&>(one)
用berlp
工作?这里没有真正的惊喜:
mem
类型为berlp&
并用berlp&
初始化所以非const
成员都按预期工作。使用
derp<berlp const&>
时并传递berlp&
模板构造函数是一个完美的匹配,显然是被选中的。berlp const&
类型的成员变量刚刚用berlp&
初始化隐式转换为berlp const&
.这也不足为奇。
我认为您对引用折叠规则有点困惑。放置const
放在错误的位置也无济于事:把它放在正确的位置实际上应该清楚大部分的困惑并且是 my preference 的一部分。把 const
向右。
关于C++ 模板右值 Ctor 与 Const 引用 Ctor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19107851/