为什么转发引用不是const?

6

正向引用是用来将参数转发到另一个函数的,是吗?那为什么它不是const的呢?

template <typename T>
void func(const T&&);

非 const 引用允许函数修改其参数(而不仅仅是转发它们)。

有趣的问题,这种签名可能没有用处。你可以使用 T&& 或者 T const&。https://youtu.be/ZbVCGCy3mGQ?t=1194 - alfC
@Eljay 我不是在质疑这个,只是想澄清一下,删除不算修改,对吧? - Ted Lyngmo
删除操作会使得被删除的函数可以用于“最佳匹配”检查。 - Eljay
@TedLyngmo “删除”是指针上的一种操作。无论指针本身是否为const,或者它是一个指向const的指针,在任何情况下都可以被删除(假设它是一个可以被删除的指针值;即它是由“new”返回的有效指针值)。 - eerorika
谢谢 - 很高兴在评论中看到这个。我之前觉得有点奇怪,所以想最好记录下来。 - Ted Lyngmo
显示剩余3条评论
1个回答

11
为什么转发引用不是const?
因为希望能够移动完美转发的xvalue。通常情况下,无法从const引用中移动对象,因为移动构造函数的参数需要是非const的。
此外,希望能够将lvalue绑定到转发引用中。无法将lvalue绑定到const rvalue引用const T&&中,它根本不是转发引用1
如果您不希望移动参数,而只是想要对其进行常量引用,则不需要转发引用。在这种情况下,const lvalue引用就足够了。
示例:
struct S {
     S() = default;
     S(S&&) = default;      // move
     S(const S&) = default; // copy
};

void foo(S fooarg);

template <typename T>
void bar(T&& bararg) { // forwarding reference
    foo(std::forward<T>(bararg));
}

// call site
S s;
bar(s);            // 1 copy
bar(S{});          // 2 move
bar(std::move(s)); // 3 move

在第二和第三种情况下,我们希望将bararg移动到fooarg,而在第一种情况下,我们希望将其复制。通过转发引用可以实现这一点。常量右值引用无法实现此功能,因为无法将常量引用传递给移动构造函数。
常量右值引用很少有用。标准库在少数情况中使用它们。
template <class T> void as_const(const T&&) = delete;
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

这些被删除的重载函数是为了防止使用临时变量(rvalue)调用函数。 const 会防止参数成为转发引用,从而绑定到任何东西并使任何调用都被删除。
constexpr const T&& optional::operator*() const&&;
constexpr const T&& optional::value() const &&;

template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);

template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;

上面的代码中,const rvalue引用被用作包装器访问包装值时的返回类型,这样可以维护包装值的值类别和常量性。
标准(草案)指出: [temp.deduct.call]...转发引用是对未带cv限定符的模板参数的rvalue引用,并且不表示类模板的模板参数(在类模板参数推断期间[over.match.class.deduct])。如果P是一个转发引用,并且参数是一个左值,则对于类型推断,使用“指向A的左值引用”代替A。

你能否添加一个部分来解释从一个对象移动可能如何改变对象?(例如,使用std::vector或std::string将数据指针设置为null) - Alecto Irene Perez
1
我对你试图表达的意思有一些模糊的感觉(我在xvalue命名约定上迷失了方向),但是可以这样说,ref(const T&&)是为了禁止从临时对象进行构造吗? - alfC
2
@alfC 是的,ref(const T&&) = delete 是用来禁止从临时对象(即rvalue)进行构建操作的。如果是 ref(T&&) = delete 的话,那么它将防止所有的使用,因为转发引用会绑定到任何东西上。 - eerorika
你知道一个例子,在这个例子中 T const&& 情况没有被未实现或删除吗? - alfC
@alfC 请查看答案中的可选项、变量和元组示例。 - eerorika

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接