C++:通用(转发)引用中不允许常量
C++: const not allowed in universal (forwarding) reference
我正在阅读这个堆栈溢出答案,其中给出了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 是右值引用。
从上面我们可以看出,实际上没有办法声明一个只接受非常量右值引用的模板。
- 为什么 Clang 不允许"and"作为函数名称?
- 为什么在我的函数类型后使用引用运算符 (&) 允许我修改它返回的值?
- 不允许在向量中添加更多元素
- std::带有自定义缓冲区的 iostream 不允许我写入
- 如果 copts 不允许系统路径,如何引用外部依赖项使用的系统库?
- ctypes 不允许多次取消对指针的引用
- C++:通用(转发)引用中不允许常量
- 为什么我们不允许将纯引用参数传递给 std::thread,但允许传递原始指针?
- 为什么标准不允许通过引用捕获不完整的异常类型?
- std::function 中不允许引用返回类型吗?
- 如果不允许我分配 rvalues 来引用为什么以下代码片段有效,这在内部如何工作?
- 为什么不允许将右值引用绑定到非常数引用,而允许在其中一个上调用非常数成员函数
- C++ 不允许我在以前工作后通过引用将 vector 传递给另一个函数
- 为什么不允许全局的结构/联合子项作为模板引用参数 - 而是全局变量本身
- 为什么C++不允许重新绑定引用?
- 不允许指向不完整类类型的指针.(与相互引用的类相结合.)
- 允许使用l值引用,不允许使用r值引用作为函数参数
- 在ISO c++ 2003标准中不允许对引用的可视化引用,但为什么编译器允许
- G++ 不允许在 lambda 中通过引用对 const 对象进行广义捕获?
- 不允许绑定到初始化列表中的引用的临时存在于actor结束之后的理由是什么?