我正在阅读this stackoverflow answer,其中给出了const T&&
不是通用(转发)引用的一个原因:
允许const T &&充当转发引用,将使得无法重载仅将右值引用作为参数的模板函数。
我不知道这意味着什么。我猜这意味着有两个相同功能(模板)的重载,其中一个以参数const T&&
为参数。我还假设这些重载之一将始终被调用,而其他重载将永远不会被调用。
如果我的假设正确,那么两个重载函数是什么? 或者如果我错了,引用的段落实际上是什么意思?
谢谢。
答案 0 :(得分:3)
据我所知,您引用的答案部分是准确的,但却具有误导性。
首先,重要的是要阐明 rvalue引用和转发引用是不同的东西,它们只是共享相同的符号&&
。这是否是一件好事,有待辩论。
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-qualification是告诉编译器将推导类型当作 rvalue引用的唯一方法。而不是转发参考。这是您引用其他答案的意思。
允许const T &&充当转发引用,将使得无法重载仅将右值引用作为参数的模板函数。
我之所以说这具有误导性,是因为这意味着如果我们超载模板以接受const T&&
,那么对于所有 右值引用不考虑简历资格。事实并非如此。
在下面的代码中,我们可以看到foo
接受 const右值引用,但由于它不是转发引用,因此没有其他内容。
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)
接受:常量右值参考。 Val是右值参考。
接受:常量右值参考。 Val是右值参考。
这似乎支持该报价,因为它接受 const rvalue引用并将 rvalue引用转换为 const rvalue引用。但是,没有过载。如果我们引入重载,我们可以看到它只接受 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是右值参考。
接受:常量右值参考。 Val是右值参考。
从上面我们可以看到,实际上没有办法声明仅仅接受非常量右值引用的模板。