C++:通用(转发)引用中不允许常量

C++: const not allowed in universal (forwarding) reference

本文关键字:引用 不允许 常量 转发 通用 C++      更新时间:2023-10-16

我正在阅读这个堆栈溢出答案,其中给出了const T&&不是通用(转发(引用的一个原因:

允许 const T&& 表现为转发引用,使得仅将右值引用作为参数的模板函数无法重载。

我不知道这意味着什么。我想这意味着具有同一函数(模板(的两个重载,其中一个作为参数const T&&。我还假设其中一个重载将始终被调用,而另一个永远不会被调用。

如果我的假设是正确的,那么两个重载函数是什么? 或者如果我错了,引用的段落实际上是什么意思?

谢谢。

据我所知,您引用的答案部分是准确的,但具有误导性。

首先,重要的是要澄清右引用和转发引用不是一回事,它们只是共享相同的符号&&。这是否是一件好事还有待商榷。

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&&那么无论 cv 资格如何,所有右值引用都将首选此重载。事实并非如此。

在下面的代码中,我们可以看到bar接受const 右值引用,但不接受其他任何引用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(

接受:常量右值引用。 Val 是右值引用。

接受:常量右值引用。 Val 是右值引用。

这似乎支持引用,因为bar接受常量右值引用并将右值引用转换为常量右值引用。但是没有超载发生。如果我们引入重载,我们可以看到bar只接受常量右值引用

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 是右值引用。

从上面我们可以看出,实际上没有办法声明一个接受非常量右值引用的模板。

相关文章: