C++:通用(转发)引用中不允许使用 const

use*_*768 5 c++ forwarding-reference

我正在阅读这个 stackoverflow 答案const T&&,其中给出了不是通用(转发)参考的一个原因:

允许 const T&& 充当转发引用,将无法重载仅采用右值引用作为参数的模板函数。

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

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

谢谢。

Fib*_*les 4

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

\n

首先,需要澄清的是,右值引用转发引用不是同一件事,它们只是共享相同的符号&&。这是否是一件好事还有待争论。

\n
template <typename T>\nvoid foo(T&&); // deduced type == forwarding reference\n\nvoid foo(int&&); // explicit type == rvalue reference\n
Run Code Online (Sandbox Code Playgroud)\n

够简单的。那么为什么下面的内容不是转发参考呢?

\n
template <typename T>\nvoid foo(const T&&); // const rvalue reference despite deduced type\n
Run Code Online (Sandbox Code Playgroud)\n

我能给你的最好答案是“因为”。这似乎是标准委员会的一个完全武断的决定。我看不出为什么const T&& 不能成为转发参考;这并不是因为标准这么说的。

\n

\xc2\xa714.8.2.1/ 从函数调用中推导模板参数 [temp.deduct.call]

\n
\n

转发引用是对 cv 不合格模板参数的右值引用。

\n
\n

不管为什么会出现这种情况,很明显,添加 cv 限定是告诉编译器将推导类型视为右值引用而不是转发引用的唯一方法。这就是你引用另一个答案所表达的观点。

\n
\n

允许 const T&& 充当转发引用,将无法重载仅采用右值引用作为参数的模板函数。

\n
\n

我之所以说这是误导性的,是因为它意味着如果我们重载一个模板来接受,那么无论 cv 资格如何,所有右值引用const T&&都会首选此重载。不是这种情况。

\n

在下面的代码中,我们可以看到bar接受const 右值引用,但不接受其他任何内容,因为val不是转发引用

\n
struct Non_POD\n{\n    Non_POD(int i) : m_i(i) { }\n    int m_i;\n};\n\nNon_POD foo() { return {0}; }\n\nconst Non_POD const_foo() { return {0}; }\n\ntemplate <typename T>\nvoid bar(const T&& val)\n{\n    std::cout << "Accepts: const rvalue ref. ";\n    if constexpr (std::is_rvalue_reference_v<decltype(val)>)\n    {\n        std::cout << "Val is rvalue reference.\\n";\n    }\n    else if constexpr (std::is_lvalue_reference_v<decltype(val)>)\n    {\n        std::cout << "Val is lvalue reference.\\n";\n    }\n    else\n    {\n        std::cout << "Val is lvalue.\\n";\n    }\n\n    std::cout << std::endl;\n}\n\nint main()\n{\n    bar(foo());\n    bar(const_foo());\n    Non_POD x(0);\n    //bar(x); // error\n}\n
Run Code Online (Sandbox Code Playgroud)\n

预期输出(GCC 7.1)

\n
\n

接受:const 右值引用。Val 是右值引用。

\n

接受:const 右值引用。Val 是右值引用。

\n
\n

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

\n
struct Non_POD\n{\n    Non_POD(int i) : m_i(i) { }\n    int m_i;\n};\n\nNon_POD foo() { return {0}; }\n\nconst Non_POD const_foo() { return {0}; }\n\ntemplate <typename T>\nvoid bar(const T&& val)\n{\n    std::cout << "Accepts: const rvalue ref. ";\n    if constexpr (std::is_rvalue_reference_v<decltype(val)>)\n    {\n        std::cout << "Val is rvalue reference.\\n";\n    }\n    else if constexpr (std::is_lvalue_reference_v<decltype(val)>)\n    {\n        std::cout << "Val is lvalue reference.\\n";\n    }\n    else\n    {\n        std::cout << "Val is lvalue.\\n";\n    }\n\n    std::cout << std::endl;\n}\n\ntemplate <typename T>\nvoid bar(T&& val)\n{\n    std::cout << "Accepts: forwarding ref. ";\n    if constexpr (std::is_rvalue_reference_v<decltype(val)>)\n    {\n        std::cout << "Val is rvalue reference.\\n";\n    }\n    else if constexpr (std::is_lvalue_reference_v<decltype(val)>)\n    {\n        std::cout << "Val is lvalue reference.\\n";\n    }\n    else\n    {\n        std::cout << "Val is lvalue.\\n";\n    }\n\n    std::cout << std::endl;\n}\n\nint main()\n{\n    Non_POD x(0);\n    const Non_POD cx(0);\n\n    bar(x);\n    bar(cx);\n    bar(Non_POD(0));\n    bar(foo());\n    bar(const_foo());\n}\n
Run Code Online (Sandbox Code Playgroud)\n

预期输出(GCC 7.1)

\n
\n

接受:转发参考。Val 是左值引用。

\n

接受:转发参考。Val 是左值引用。

\n

接受:转发参考。Val 是右值引用。

\n

接受:转发参考。Val 是右值引用。

\n

接受:const 右值引用。Val 是右值引用。

\n
\n

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

\n