据我所知,您引用的答案部分准确但具有误导性。
首先要澄清的是,右值引用和转发引用不是一回事,它们只是共享相同的符号&&。这是否是一件好事还有待商榷。
template <typename T>
void foo(T&&); // deduced type == forwarding reference
void foo(int&&); // explicit type == rvalue reference
足够简单。那么为什么以下不是转发参考?
template <typename T>
void foo(const T&&); // const rvalue reference despite deduced type
我能给你的最佳答案是“因为”。这似乎是标准委员会的一个完全武断的决定。我看不出为什么const T&& 不能成为一个转发参考;这不是因为标准是这样说的。
§14.8.2.1/ 从函数调用中推导出模板参数 [temp.deduct.call]
转发引用是对 cv 非限定模板参数的右值引用。
不管为什么会出现这种情况,很明显,添加 cv 限定是告诉编译器将推导的类型视为 右值引用的唯一方法而不是转发参考。您从其他答案中引用的内容是什么意思。
允许 const T&& 充当转发引用,将无法重载仅将右值引用作为参数的模板函数。
我说这是误导的原因是因为它暗示如果我们重载模板以接受const T&&,那么无论所有 右值引用都将首选此重载简历资格。事实并非如此。
在下面的代码中,我们可以看到bar 接受const rvalue 引用,但没有别的,因为val 不是转发引用。
struct Non_POD
{
Non_POD(int i) : m_i(i) { }
int m_i;
};
Non_POD foo() { return {0}; }
const Non_POD const_foo() { return {0}; }
template <typename T>
void bar(const T&& val)
{
std::cout << "Accepts: const rvalue ref. ";
if constexpr (std::is_rvalue_reference_v<decltype(val)>)
{
std::cout << "Val is rvalue reference.\n";
}
else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
{
std::cout << "Val is lvalue reference.\n";
}
else
{
std::cout << "Val is lvalue.\n";
}
std::cout << std::endl;
}
int main()
{
bar(foo());
bar(const_foo());
Non_POD x(0);
//bar(x); // error
}
预期输出 (GCC 7.1)
接受:const rvalue ref。 Val 是右值引用。
接受:const rvalue ref。 Val 是右值引用。
这似乎支持引用,因为bar 接受 const rvalue references 并将 rvalue references 转换为 const rvalue references。但是没有超载发生。如果我们引入重载,我们可以看到 bar 只接受 const rvalue 引用。
struct Non_POD
{
Non_POD(int i) : m_i(i) { }
int m_i;
};
Non_POD foo() { return {0}; }
const Non_POD const_foo() { return {0}; }
template <typename T>
void bar(const T&& val)
{
std::cout << "Accepts: const rvalue ref. ";
if constexpr (std::is_rvalue_reference_v<decltype(val)>)
{
std::cout << "Val is rvalue reference.\n";
}
else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
{
std::cout << "Val is lvalue reference.\n";
}
else
{
std::cout << "Val is lvalue.\n";
}
std::cout << std::endl;
}
template <typename T>
void bar(T&& val)
{
std::cout << "Accepts: forwarding ref. ";
if constexpr (std::is_rvalue_reference_v<decltype(val)>)
{
std::cout << "Val is rvalue reference.\n";
}
else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
{
std::cout << "Val is lvalue reference.\n";
}
else
{
std::cout << "Val is lvalue.\n";
}
std::cout << std::endl;
}
int main()
{
Non_POD x(0);
const Non_POD cx(0);
bar(x);
bar(cx);
bar(Non_POD(0));
bar(foo());
bar(const_foo());
}
预期输出 (GCC 7.1)
接受:转发参考。 Val 是左值引用。
接受:转发参考。 Val 是左值引用。
接受:转发参考。 Val 是右值引用。
接受:转发参考。 Val 是右值引用。
接受:const rvalue ref。 Val 是右值引用。
从上面我们可以看出,实际上没有办法声明一个仅接受非const右值引用的模板。