为啥 const 方法不能返回非常量引用?

Posted

技术标签:

【中文标题】为啥 const 方法不能返回非常量引用?【英文标题】:Why can't a const method return a non-const reference?为什么 const 方法不能返回非常量引用? 【发布时间】:2014-06-21 22:50:55 【问题描述】:

为什么下面的方法getRanks()编译不出来,如何优雅的修复呢?

我想要做的就是定义一个返回成员引用的成员访问器方法。参考不是const,因为我稍后可能会修改它所指的内容。但是由于成员方法没有修改对象,所以我声明为const。编译器(clang,std=c++11)然后坚持认为存在“删除限定符”的“引用绑定”。但我不会放弃预选赛,是吗?如果我是,为什么:

struct teststruct
  vector<int> ranks;
  vector<int>& getRanks()const
    return ranks;
  
;

现在,如果我更改 return 语句以丢弃 const,则代码编译:

return const_cast<vector<int>&>(ranks);

但是“ranks”首先不应该是 const,我不明白为什么我需要 const_cast const 。我什至不知道这样做是否安全。

不管怎样,有没有更清洁的方法可以写这个方法?有人可以解释为什么这种简单的常识方法会失败吗?我确实想声明getRanks() 方法“const”,以便我可以从其他const 方法中调用它。

【问题讨论】:

teststruct const x; x.getRanks().emplace_back(); -> UB. 【参考方案1】:

const 成员函数背后的想法是您应该能够在 const 对象上调用它们。 const 函数不能修改对象。

假设你有一堂课

class A

   int data;
   void foo() const
   
   
;

关于对象和函数调用:

A const a;
a.foo();

A::foo 内部,this-&gt;data 被视为其类型为int const,而不是int。因此,您无法在A:foo() 中修改this-&gt;data

以您的示例为例,getRanks() 中的this-&gt;ranks 类型将被视为const vector&lt;int&gt; 而不是vector&lt;int&gt;。由于不允许将const vector&lt;int&gt; 自动转换为vector&lt;int&gt;&amp;,因此当您将函数定义为时编译器会报错:

vector<int>& getRanks()const
    return ranks;
  

如果你将函数定义为:

const vector<int>& getRanks()const
    return ranks;
  

因为const vector&lt;int&gt; 可以自动转换为const vector&lt;int&gt;&amp;

【讨论】:

【参考方案2】:

ranksconst,因为封闭对象 (*this) 是 const,因此您必须返回对 std::vector&lt;int&gt; const 的引用。

如果你想让客户端修改向量(从而影响成员),那么getter不应该是const。请注意,getter 无论如何都是愚蠢的,因为 ranks 已经是公共数据成员。

【讨论】:

如果 getter 不是 const,则不能从 const 方法调用。但我想这样做,我想有时从 const 方法调用它,有时从 non_const 方法调用它。 @kdog 然后创建两个 getter,一个 const 和一个 non-const。 std::vector::operator[].【参考方案3】:

您正在返回对ranks 的引用,它是teststruct 的成员。这意味着任何获得此引用的人都可以修改teststruct 对象的内部结构。所以const 是个谎言。

不要抛弃const。取而代之的是,决定您是希望函数为 const 并返回 ranksconst 引用的副本,还是非 const 并返回可变引用。如有必要,您始终可以同时拥有两者。

【讨论】:

但是等等。我认为方法声明中分号之前的“const”只是意味着该方法 ITSELF 没有修改它是成员函数的对象。如果我记得的话,这就是 Stroustrup 书中所说的。我并不是说从现在到将来返回的任何使用都不会修改对象。我只是声明函数本身不会。而且我确实希望返回一个可以修改的参考,而不是一个副本! const_cast 的想法也可以安全使用吗? @kdog 好吧,这将允许该方法间接导致对象被修改。 const_cast 的想法并不安全,因为编译器可以根据它认为不会被修改的事实做出假设。 与其说“const 是个谎言”,不如说“const/return 类型破坏了 const 的正确性”。 不,编译器应该在 v.push_back(42) 行给出错误,而不是在 getRanks() 行......尽管我开始看到这个问题。为什么这对我来说如此令人困惑和违反直觉,而对其他人来说却很明显;-) @kdog 成员函数的const 表示调用该函数的对象是const。换句话说,this 指针是 const。因为this 是常量,所以this-&gt;anything 也是constgetRanks 不需要返回 const &amp;,但需要返回具有返回类型的类型的对象。【参考方案4】:

它会删除限定符,因为您返回对 ranks 的引用。之后的任何代码都可以修改排名。例如:

auto v = teststruct.getRanks();
v[1] = 5; //assuming v.size() > 1

您可以通过返回副本来解决此问题:

vector<int> getRanks() const

const 参考:

const vector<int>& getRanks() const

如果您希望 ranks 即使在 const 对象中也可以更改,您可以这样做:

mutable vector<int> ranks;

【讨论】:

不,这并不能解决问题,因为我以后可能想修改 getRanks() 返回的引用。我需要两个 getRank,一个 const 一个不需要吗?我只想声明 getRanks() ITSELF 不会修改对象。可以在 const 方法中调用它。 不,我不希望排名是可变的,我希望进行 const 检查。 那么听起来vector&lt;int&gt;&amp; getRanks();const vector&lt;int&gt;&amp; getRanks() const;这两个重载就是你想要的。

以上是关于为啥 const 方法不能返回非常量引用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能将 const 左值引用绑定到返回 T&& 的函数?

为啥不调用具有 const 引用返回值的重载方法?

从 const 引用初始化非常量对象时防止复制

测试页面

c++中重载输出操作符,为啥要返回引用

为啥 const rvalue 限定 std::optional::value() 返回 const rvalue 引用?