【问题标题】:Mutable Lvalue Reference of Parameter Passed by const Referenceconst 引用传递的参数的可变左值引用
【发布时间】:2019-09-15 09:12:44
【问题描述】:

我刚刚发现了一个有趣的案例:我通过const reference 传递了一个对象,但我仍然能够修改它的成员(恰好是lvalue reference)。 Following 就是一个例子:

#include <iostream>
#include <string>

struct PersonRef
{
  PersonRef(std::string& name_) : _name(name_) {}
  std::string& _name; // <==== IMPORTANT: it is a reference
};

void testRef(const PersonRef& pr) {
  std::string& name = pr._name; // binding to lvalue reference? How does it even compile?
  name = "changed!";
}

int main() {
  std::string name = "trivial_name";
  PersonRef pr{name};

  std::cout << pr._name << "\n"; // prints: trivial_name
  testRef(pr);
  std::cout << pr._name << "\n"; // prints: changed!
}

我曾经认为如果参数由const ref 传递,那么对象是不可变的,但这里似乎不是这样。有人可以解释一下吗?谢谢!

【问题讨论】:

  • 1) 传递 const &amp; 并不意味着对象是不可变的,这意味着引用具有 const 限定符,这将影响重载决议 2) 你永远不会修改它的成员,你修改一个非其成员引用的 const 限定对象;请注意,即使您使用Person const &amp; 访问_name,它也不会将_name 的类型调整为std::string const &amp;。如果_name 是指针std::string *,那么const 限定符将应用于指针,因此它将变为std::string * const,但默认情况下引用不可重新绑定,因此std::string &amp; const 什么也不做。
  • 好的,所以也许,基本问题是:1) 如果我通过const PersonRef&amp; 访问_name,为什么它的类型没有调整为const std::string&amp;。这是我所预料的。 2) std::string & const 什么都不做 是什么意思?如果我将_name 声明为const std::string&amp; ,则此代码无法编译,因为在将其绑定到函数内部的non-const lvalue reference 时,我无法忽略它的cost-ness
  • 通过 const 限定引用访问类成员确实会为类成员添加 const 限定符。但是_name 成员是一个引用,它具有一级间接性。 const 限定符不通过该间接杠杆传播,它仅直接应用于类成员。访问_name 会产生std::string &amp; const 而不是std::string const &amp;,正如您所期望的(您似乎对const 位置感到困惑)因为引用不可重新绑定std::string &amp; const 的行为方式与std::string &amp; 完全相同。
  • 谢谢,很有道理!我对std::string &amp; conststd::string const &amp; 感到困惑

标签: c++


【解决方案1】:

请注意,在const成员函数中,数据成员_name本身将被视为const。这对_name 没有任何影响,因为它是一个参考,不能是const-qualified。 (从某种意义上说,引用总是const,你不能修改引用本身,初始化后引用不能反弹到其他对象。)

另一方面,被引用的对象不会变成const,所以还是可以修改的,_name不会变成const std::string&amp;(引用const)。

类似的事情发生在指针成员上;在const 成员函数中,它们成为const 指针,但不是指向const 的指针;如果它们从一开始就不是consts,你仍然可以修改指向的对象。

【讨论】:

    【解决方案2】:

    考虑一个不同的例子。

    假设你有struct A {int *x;};
    当通过const A &amp;ref 访问时,x 将具有类型int *const - 一个指向(非constintconst 指针。

    如您所见,const 不是递归地添加到指针中。它只添加在顶层。


    回到您的代码。严格来说,const PersonRef&amp; pr 不是 const 引用。这是一个非const 引用 const PersonRef

    (理论上,const 引用将被写为 PersonRef &amp;const pr。但由于引用不可重新绑定开始,添加 const 不会做任何事情,因此是不允许的。所以技术上的引用永远不会是const,即使您无法重新绑定它们。)

    编译器无法将const-ness 添加到std::string&amp; _name,因为引用不能是const。而且它不会递归地将 const-ness 添加到引用的类型中,仅仅是因为这就是语言的工作方式。


    如果您不希望这种行为,请创建std::string&amp; _name private 并添加一对const 和非const 访问器:

    class PersonRef
    {
        std::string &_name;
      public:
        PersonRef(std::string& name_) : _name(name_) {}
        std::string &name() {return _name;}
        const std::string &name() const {return _name;}
    
    };
    

    【讨论】:

    • 好的,据我了解:对 const T 的非 const 引用应该是 1)它的指针 const,2)它的非引用成员 const 但 3)它不能将其引用 const 作为 references 永远不会是 const 对吗?
    • @aniliitb10 引用不可重新绑定,即它们始终是 const,因此省略了 const 限定符。
    • 是的,谢谢,我发现了一个类似的问题,它描述了引用是inherently const。当我意识到 T 的 const-ness 不是递归应用时,这很有意义!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-09
    • 2013-01-10
    • 1970-01-01
    • 2020-01-23
    • 2016-10-10
    • 1970-01-01
    相关资源
    最近更新 更多