태그 보관물: const

const

Const C ++ DRY 전략 C ++ 항목

사소한 C ++ const 관련 중복을 피하기 위해 const_cast는 작동하지만 non-const를 반환하는 개인 const 함수는 그렇지 않은 경우가 있습니까?

Scott Meyers의 Effective C ++ 항목 3에서 정적 캐스트와 결합 된 const_cast는 중복 코드를 피하는 효과적이고 안전한 방법 일 수 있다고 제안합니다.

const void* Bar::bar(int i) const
{
  ...
  return variableResultingFromNonTrivialDotDotDotCode;
}
void* Bar::bar(int i)
{
  return const_cast<void*>(static_cast<const Bar*>(this)->bar(i));
}

Meyers는 const 함수를 non-const 함수로 호출하는 것이 위험하다고 설명합니다.

아래 코드는 다음과 같은 반례입니다.

  • Meyers의 제안과 달리, 때로는 정적 캐스트와 결합 된 const_cast가 위험합니다
  • 때로는 const 함수가 비 const를 호출하는 것이 덜 위험합니다.
  • 때로는 const_cast를 사용하는 두 가지 방법으로 잠재적으로 유용한 컴파일러 오류 숨기기
  • const_cast를 피하고 추가 const 개인 멤버가 const가 아닌 것을 반환하는 것은 또 다른 옵션입니다.

코드 복제를 피하는 const_cast 전략 중 하나가 모범 사례로 간주됩니까? 대신 개인 방법 전략을 원하십니까? const_cast는 작동하지만 개인 메소드는 작동하지 않는 경우가 있습니까? 다른 옵션 (복제 외에)이 있습니까?

const_cast 전략에 대한 나의 관심은 코드를 작성할 때 코드가 정확하더라도 나중에 유지 관리 중에 코드가 잘못 될 수 있고 const_cast가 유용한 컴파일러 오류를 숨길 수 있다는 것입니다. 일반적인 개인 기능이 일반적으로 더 안전한 것 같습니다.

class Foo
{
  public:
    Foo(const LongLived& constLongLived, LongLived& mutableLongLived)
    : mConstLongLived(constLongLived), mMutableLongLived(mutableLongLived)
    {}

    // case A: we shouldn't ever be allowed to return a non-const reference to something we only have a const reference to

    // const_cast prevents a useful compiler error
    const LongLived& GetA1() const { return mConstLongLived; }
    LongLived& GetA1()
    {
      return const_cast<LongLived&>( static_cast<const Foo*>(this)->GetA1() );
    }

    /* gives useful compiler error
    LongLived& GetA2()
    {
      return mConstLongLived; // error: invalid initialization of reference of type 'LongLived&' from expression of type 'const LongLived'
    }
    const LongLived& GetA2() const { return const_cast<Foo*>(this)->GetA2(); }
    */

    // case B: imagine we are using the convention that const means thread-safe, and we would prefer to re-calculate than lock the cache, then GetB0 might be correct:

    int GetB0(int i) { return mCache.Nth(i); }
    int GetB0(int i) const { return Fibonachi().Nth(i); }

    /* gives useful compiler error
    int GetB1(int i) const { return mCache.Nth(i); } // error: passing 'const Fibonachi' as 'this' argument of 'int Fibonachi::Nth(int)' discards qualifiers
    int GetB1(int i)
    {
      return static_cast<const Foo*>(this)->GetB1(i);
    }*/

    // const_cast prevents a useful compiler error
    int GetB2(int i) { return mCache.Nth(i); }
    int GetB2(int i) const { return const_cast<Foo*>(this)->GetB2(i); }

    // case C: calling a private const member that returns non-const seems like generally the way to go

    LongLived& GetC1() { return GetC1Private(); }
    const LongLived& GetC1() const { return GetC1Private(); }

  private:
    LongLived& GetC1Private() const { /* pretend a bunch of lines of code instead of just returning a single variable*/ return mMutableLongLived; }

    const LongLived& mConstLongLived;
    LongLived& mMutableLongLived;
    Fibonachi mCache;
};

class Fibonachi
{ 
    public:
      Fibonachi()
      {
        mCache.push_back(0);
        mCache.push_back(1);
      }

      int Nth(int n) 
      {
        for (int i=mCache.size(); i <= n; ++i)
        {
            mCache.push_back(mCache[i-1] + mCache[i-2]);
        }
        return mCache[n];
      }

      int Nth(int n) const
      {
          return n < mCache.size() ? mCache[n] : -1;
      }
    private:
      std::vector<int> mCache;
};

class LongLived {};


답변

반환 된 ptr / reference가 const인지에 따라 다른 const와 non-const 멤버 함수를 구현할 때 가장 좋은 DRY 전략은 다음과 같습니다.

  1. 접근 자를 작성하는 경우 접근자가 정말로 필요한지 고려하십시오. cmaster ‘s answerhttp://c2.com/cgi/wiki?AccessorsAreEvil을 참조하십시오 .
  2. 코드가 사소한 경우 코드를 복제하십시오 (예 : 멤버를 반환)
  3. const 관련 중복을 피하기 위해 const_cast를 사용하지 마십시오
  4. 사소한 중복을 피하려면 const 및 non-const 공용 함수가 호출하는 non-const를 리턴하는 개인 const 함수를 사용하십시오.

예 :

public:
  LongLived& GetC1() { return GetC1Private(); }
  const LongLived& GetC1() const { return GetC1Private(); }
private:
  LongLived& GetC1Private() const { /* non-trivial DRY logic here */ }

이것을 const가 아닌 const를 리턴 하는 private const 함수 라고하자 .

이것은 컴파일러가 잠재적으로 유용한 검사를 수행하고 const 관련 오류 메시지를보고 할 수 있도록하는 동시에 중복을 피하기위한 최선의 전략입니다.


답변

그렇습니다. 맞습니다. const-correctness를 시도하는 많은 C ++ 프로그램은 DRY 원칙을 심각하게 위반하고 있으며, non-const를 반환하는 개인 구성원조차도 편의를 위해 너무 복잡합니다.

그러나 한 가지 관찰은 그리워합니다. const-correctness로 인한 코드 복제는 데이터 멤버에 다른 코드 액세스를 제공하는 경우에만 문제가됩니다. 이것은 그 자체로 캡슐화를 위반하는 것입니다. 일반적으로 이러한 종류의 코드 복제는 대부분 간단한 접근 자에서 발생합니다 (결국 기존 멤버에 대한 액세스를 전달하는 경우 반환 값은 일반적으로 계산 결과가 아님).

내 경험은 좋은 추상화에는 접근자가 포함되지 않는 것입니다. 결과적으로 데이터 멤버에 대한 액세스를 제공하는 대신 실제로 무언가를 수행하는 멤버 함수를 정의하여이 문제를 피할 수 있습니다. 데이터 대신 행동을 모델링하려고합니다. 이것의 주요 목적은 객체를 데이터 컨테이너로 사용하는 대신 실제로 클래스와 개별 멤버 함수 모두에서 추상화를 얻는 것입니다. 그러나이 스타일은 대부분의 코드에서 일반적으로 사용되는 수많은 const / const 반복적 인 한 줄 접근자를 피하는 데 매우 성공적입니다.


답변