【问题标题】:"get() const" vs. "getAsConst() const"“get() const”与“getAsConst() const”
【发布时间】:2026-01-04 10:35:02
【问题描述】:

有人告诉我他们团队中的 C++ 风格差异。我对这个问题有自己的看法,但我会对来自每个人的proscons感兴趣。

所以,如果你有一个类属性,你想通过两个 getter 公开,一个是读/写,另一个是只读的(即没有 set 方法)。至少有两种方法:

class T ;

class MethodA
{
   public :
      const T & get() const ;
      T & get() ;

      // etc.
} ;

class MethodB
{
   public :
      const T & getAsConst() const ;
      T & get() ;

      // etc.
} ;

每种方法的优缺点是什么?

我对 C++ 技术/语义方面的原因更感兴趣,但也欢迎风格方面的原因。

请注意,MethodB 有一个主要的技术缺陷(提示:在通用代码中)。

【问题讨论】:

  • 我认为这个例子有错字?
  • 这是 C++ 伪代码。重要的是 MethodA 上的只读 getter 称为“get”,MethodB 上的读/写 getter 称为“getAsConst”。没有定义 T 的事实对于本次讨论是次要的。不过,你是对的。我会纠正那个,以及缺少的“公众”...... :-)
  • 啊,我以为我看到 MethodA 和 MethodB 与第一个方法的命名相同的错字。鉴于问题的措辞,我希望一个 MethodB 没有“T & get() ;”作为一种方法。

标签: c++ coding-style constants


【解决方案1】:

C++ 应该完全能够在几乎所有情况下处理方法 A。我一直用它,从来没有遇到过问题。

在我看来,方法 B 是违反 OnceAndOnlyOnce 的案例。而且,现在你需要弄清楚你是否在处理 const 引用来编写第一次编译的代码。

我想这是一种风格上的东西——从技术上讲,它们都可以工作,但是 MethodA 使编译器工作得更难一些。对我来说,这是一件好事。

【讨论】:

    【解决方案2】:

    嗯,一方面,getAsConst 必须在'this'指针是const时调用——而不是在你想接收一个const对象时。因此,除了任何其他问题之外,它还被巧妙地命名为错误名称。 (当 'this' 是非常量时,你仍然可以调用它,但这既不在这里也不在那里。)

    忽略这一点,getAsConst 不会为您带来任何好处,并且会给使用该接口的开发人员带来不必要的负担。而不是仅仅调用“get”并知道他得到了他需要的东西,现在他必须确定他当前是否正在使用 const 变量,以及他正在抓取的新对象是否需要是 const 。之后,如果两个对象由于重构而变为非 const,他就必须关闭他的调用。

    【讨论】:

      【解决方案3】:

      就个人而言,我更喜欢第一种方法,因为它使界面更加一致。此外,对我来说 getAsConst() 听起来和 getAsInt() 一样愚蠢。

      另一方面,在返回非常量引用或指向类的数据成员的非常量指针之前,您确实应该三思而后行。这是邀请人们利用您班级的内部运作,理想情况下应该隐藏。换句话说,它打破了封装。我会使用一个 get() const 和一个 set(),并且只有在没有其他方法或真正有意义时才返回一个非常量引用,例如授予对数组元素的读/写访问权限或矩阵。

      【讨论】:

      • 封装是对的。在那种情况下,“封装违规”是这样做的权利,但这超出了本次讨论的范围。 :-)
      • 我没有注意到 getter 正在返回一个引用。你是对的,非常量 getter 可能应该按值返回。
      • 如果你按值返回,那么你的getter应该仍然是const。
      • “非 const getter”是指返回非 const 的 getter。它不应该返回对象内部的引用。你是对的,不管 getter 应该是 const (通常)。
      【解决方案4】:

      鉴于标准库设置的样式先例(即 begin() 和 begin() const 仅举一个例子),显然方法 A 是正确的选择。我质疑选择方法 B 的人的理智。

      【讨论】:

        【解决方案5】:

        所以,第一种风格通常更可取。

        我们确实在我目前正在处理的代码库中大量使用了第二种样式的变体,因为我们希望在 const 和非 const 用法之间有一个很大的区别。

        在我的具体示例中,我们有 getTessellation 和 getMutableTessellation。它是用写时复制指针实现的。出于性能方面的考虑,我们希望尽可能使用 const 版本,因此我们将名称缩短,并将其命名为不同的名称,这样人们就不会在他们不打算写的时候意外地复制。

        【讨论】:

          【解决方案6】:

          虽然您的问题似乎只针对一种方法,但我很乐意就风格提供意见。就个人而言,出于风格原因,我更喜欢前者。大多数 IDE 都会为您弹出函数的类型签名。

          【讨论】:

            【解决方案7】:

            我更喜欢第一个。当本质上做相同事情的两件事看起来相同时,它在代码中看起来更好。此外,您很少有一个非常量对象但又想调用 const 方法,所以这并不是什么大问题(在最坏的情况下,您只需要一个 const_cast)。

            【讨论】:

              【解决方案8】:

              第一个允许更改变量类型(无论是否为const),而无需进一步修改代码。当然,这意味着没有通知开发人员这可能已从预期路径更改。因此,您真正看重的是能够快速重构还是拥有额外的安全网。

              【讨论】:

                【解决方案9】:

                第二个是与匈牙利符号相关的东西,我个人喜欢,所以我会坚持 first 方法。

                我不喜欢匈牙利符号,因为它增加了我在编程中通常讨厌的冗余。这只是我的意见。

                【讨论】:

                  【解决方案10】:

                  由于您隐藏了类的名称,因此这种关于风格的思考可能适用,也可能不适用:

                  告诉这两个对象 MethodA 和 MethodB “get”还是“getAsConst”有意义吗?您会将“get”或“getAsConst”作为消息发送到任一对象吗?

                  在我看来,作为消息的发送者/方法的调用者,是获得价值的人;因此,为了响应这个“get”消息,您正在向 MethodA / MethodB 发送一些消息,其结果就是您需要获取的值。

                  示例:如果 MethodA 的调用者是 SOA 中的服务,而 MethodA 是存储库,则在服务的 get_A() 中,调用 MethodA.find_A_by_criteria(...)。

                  【讨论】:

                  • 我很困惑...我猜你错过了问题的重点。这个问题是 C++ 特有的,并且不容易在其他语言中转置(AFAIK,不是 C#,也不是 Java,也不是 JavaScript、Basic 等)。它是关于 const-ness 及其在 C++ 中的使用。
                  • 你可能是对的。我想从我的角度来看,唯一跳出来的区别是方法的名称。
                  【解决方案11】:

                  我看到的 MethodB 的主要技术缺陷是,在对其应用泛型代码时,我们必须将代码加倍以同时处理 const 和非 const 版本。例如:

                  假设 T 是一个可排序对象(即,我们可以使用运算符

                  对于 MethodA,我们只需要编写代码:

                  template <typename T>
                  T & getMax(T & p_oLeft, T & p_oRight)
                  {
                     if(p_oLeft.get() > p_oRight.get())
                     {
                        return p_oLeft ;
                     }
                     else
                     {
                        return p_oRight ;
                     }
                  }
                  

                  此代码适用于 const 对象和 T 类型的非 const 对象:

                  // Ok
                  const MethodA oA_C0(), oA_C1() ;
                  const MethodA & oA_CResult = getMax(oA_C0, oA_C1) ;
                  
                  // Ok again
                  MethodA oA_0(), oA_1() ;
                  MethodA & oA_Result = getMax(oA_0, oA_1) ;
                  

                  当我们想将这个简单的代码应用于遵循 MethodB 约定的东西时,问题就来了:

                  // NOT Ok
                  const MethodB oB_C0(), oB_C1() ;
                  const MethodB & oB_CResult = getMax(oB_C0, oB_C1) ; // Won't compile
                  
                  // Ok
                  MethodA oB_0(), oB_1() ;
                  MethodA & oB_Result = getMax(oB_0, oB_1) ;
                  

                  要让 MethodB 在 const 和 non-const 版本上工作,我们都必须使用已经定义的 getMax,但要添加以下版本的 getMax:

                  template <typename T>
                  const T & getMax(const T & p_oLeft, const T & p_oRight)
                  {
                     if(p_oLeft.getAsConst() > p_oRight.getAsConst())
                     {
                        return p_oLeft ;
                     }
                     else
                     {
                        return p_oRight ;
                     }
                  }
                  

                  结论,由于不信任编译器的 const 使用,我们负担了创建两个泛型函数的负担,而这本来应该足够了。

                  当然,如果有足够的偏执,第二个模板函数应该被称为 getMaxAsConst...因此,问题会在所有代码中传播...

                  :-p

                  【讨论】: