【问题标题】:Call nonconst member version from const [duplicate]从 const 调用非常量成员版本 [重复]
【发布时间】:2013-01-02 06:53:15
【问题描述】:

可能重复:
How do I remove code duplication between similar const and non-const member functions?

我有两个成员

A &B::GetA (int i)
{
    return *(m_C[m_AtoC[i]]);
}

const A &B::GetA (int i) const
{
    return *(m_C[m_AtoC[i]]);
}

现在我只是复制代码,但可能存在很好的方法来做到这一点。我当然不想处理从 const 到 non-const 的类型转换。

编辑:所以我想打电话给另一个成员以避免代码重复。

【问题讨论】:

  • 你想要非常量版本做什么?
  • @Beta 能够修改成员。
  • 抱歉,我错过了&
  • 一个小的#define 可以解决问题,但我猜你也不想使用它;-)

标签: c++ constants overloading


【解决方案1】:

[注意更正;很抱歉最初弄错了。]

这是使用 const_cast 的合适情况,它允许您通过将非常量函数的调用转发到相应的 const 函数来消除重复代码:

A & B::GetA(int index) 
{
    return const_cast<A &>(static_cast<B const *>(this)->GetA(index));
}

重要的是要注意,这只能在一个方向上完成:您可以根据常量实现non-const 方法,但不能反过来:对GetA() 的常量调用获得一个常量引用到有问题的对象,但由于我们有额外的信息表明它实际上可以改变对象,我们可以安全地从结果中丢弃常量。

Scott Meyer 的 Effective C++ 中甚至有一章专门介绍了这种技术。

【讨论】:

  • @AndyProwl:那又怎样。一个 const-cast 这个问题的适当解决方案。
  • @KerrekSB 除了在B const 的实例上执行时您的演员表定义不明确。反过来做,const-cast 在非const 版本中。
  • @Konrad const-casting(即玩弄 types)不是 UB。
  • 我查看了修订历史。只要 GetA 自身不发生变异(如果它确实gasp 为什么yyy),原始版本就很好。从未进行过任何突变。只有类型会受到伤害,而不是对象。该函数仍然返回一个不可修改的引用。
  • 这个解决方案似乎比原来的有很多更多代码。这真的是优势吗? :-)
【解决方案2】:

你可以这样做:

class B {
public:
    A& GetA (int index) { return GetA_impl(index); }
    const A& GetA (int index) const { return GetA_impl(index); }
private:
    A& GetA_impl (int index) const { return *(m_C[m_AtoC[i]]); }
};

我不确定在这种情况下是否真的值得付出努力,但如果可能重复的代码变得更加复杂,这可能会很有用。

【讨论】:

  • 我假设,如果 m_C 是成员,那么您将在私有方法中丢弃 const 限定符 ...
  • @Useless:取决于m_C的类型。但如果它是典型的 std::vector&lt;A*&gt;,则此代码不会丢弃限定符。
【解决方案3】:

您可以通过一些模板元编程来避免 const_cast。

// This meta function returns a const U if T is const, otherwise returns just U.
template <typename T, typename U>
make_same_const<T, U>
{
    typedef typename std::conditional<
            std::is_const<T>::value,
            typename std::add_const<U>::type,
            U
        >::type type;
};


// Generic version of a function. Constness of return type depends on
// constness of T.
// This is a static member template of B.
template <typename T>
make_same_const<T, A>::type& B::GetA(T& obj, int i)
{
    return *(obj.m_C[obj.m_AtoC[i]]);
}

A&       B::GetA(int i)       { return B::GetA(*this, i); }
A const& B::GetA(int i) const { return B::GetA(*this, i); }

【讨论】:

    【解决方案4】:

    IMO 代码(或复杂性)不足以进行重复数据删除。

    正如您在基于const_cast 的解决方案中看到的那样,强制转换表达式实际上比原始代码长

    但是,如果您确实担心更长或更复杂的表达方式,请展示出来。

    【讨论】:

      【解决方案5】:

      假设GetA()GetA() const 的主体是相同的(这意味着GetA() 不会修改*this),您可以安全地使用const_cast 来实现使用非const 的const 版本:

      const A& B::GetA() const {
        return const_cast<B*>(this)->GetA();
      }
      

      非常量 GetA() 不会修改对象,因此在 const B 对象上调用它不是未定义的。由非常量 GetA() 返回的非常量引用在从 GetA() const 返回之前转换为 const&amp;,因此也不存在未定义行为的危险。

      【讨论】:

      • 投反对票的人会自己解释吗?
      • 您的答案没有为其他答案添加任何新内容,这些答案也忽略了“我当然不想处理从 const 到非 const 的类型转换”位。
      • @melpomene 这是目前在const 版本中使用const_cast 的唯一答案(您不必使用两个)并委托给另一个GetA(),而不是重新实现。我理解 OP 的评论是指在 call 网站上投射 constness。 但点接受了。
      【解决方案6】:

      怎么样

      const A &B::GetA (int index) const
      {
          return *(const_cast<B*>(this)->GetA(index));
      }
      

      【讨论】:

      • 不是我不喜欢它,而是 OP 写道“我当然不想处理从 const 到非 const 的类型转换。”
      • @AndyProwl 我理解这意味着“在呼叫站点上”
      • 这与原版有何不同?您的意思是发布类似于 Kerrek SB 的答案吗?
      • @Angew:嗯,好吧,也许这取决于一些解释
      • @SanderDeDycker:这原始答案(请注意这是发布的第一个答案)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-09
      • 2021-08-10
      • 2016-08-17
      • 2012-01-28
      • 1970-01-01
      相关资源
      最近更新 更多