簡體   English   中英

如何從const方法生成非const方法?

[英]How to generate a non-const method from a const method?

在努力保持正確性的同時,我經常發現自己正在編寫這樣的代碼

class Bar;

class Foo {
public:
  const Bar* bar() const { /* code that gets a Bar somewhere */ }

  Bar* bar() {
    return const_cast< Bar* >(
      static_cast< const Foo* >(this)->bar());
  }
};

對於很多方法,比如bar() 編寫這些非const方法,手動調用常量方法是乏味的; 此外,我覺得我在重復自己 - 這讓我心疼。

我該怎么做才能減輕這個任務? (不允許使用宏和代碼生成器。)

編輯:除了litb的解決方案,我也喜歡我自己的解決方案。 :)

另一種方法是編寫一個調用函數的模板(使用CRTP)並從中繼承。

template<typename D>
struct const_forward {
protected:
  // forbid deletion through a base-class ptr
  ~const_forward() { }

  template<typename R, R const*(D::*pf)()const>
  R *use_const() {
    return const_cast<R *>( (static_cast<D const*>(this)->*pf)() );
  }

  template<typename R, R const&(D::*pf)()const>
  R &use_const() {
    return const_cast<R &>( (static_cast<D const*>(this)->*pf)() );
  }
};

class Bar;

class Foo : public const_forward<Foo> {
public:
  const Bar* bar() const { /* code that gets a Bar somewhere */ }
  Bar* bar() { return use_const<Bar, &Foo::bar>(); }
};

請注意,調用沒有丟失性能:由於成員指針作為模板參數傳遞,因此可以像往常一樣內聯調用。

使用以下技巧:


class Bar;
class Foo {
public:  
  Bar* bar() { 
    // non-const case
    /* code that does something */ 
  }  
  const Bar* bar() const {    
      return This().bar();  // use non-const case
   }

private:
  //trick: const method returns non-const reference
  Foo & This() const { return const_cast<Foo &>(*this); } 
};

注意,可以使用獨特的功能, This對於任何常量/非const功能。

沒有static_cast的替代解決方案(但我更喜歡第一個):

class Bar;
class Foo {
public:  
  const Bar* bar() const { /* code that does something */ }  
  Bar* bar() { return const_cast<Bar*>(cthis().bar()); } // use const case
private:
  const Foo & cthis() const { return *this; } 
};

你可以這樣做:

class Bar;

class Foo {
public:
  const Bar* bar() const { return getBar(); }

  Bar* bar() {
   return getBar();
  }

  private:
    Bar* getBar() const {/* Actual code */ return NULL;}
};

我個人的感覺是,如果你這么做,你的設計中有一些可疑的東西。 在我不得不做類似的事情的情況下,我通常通過方法可變來訪問這個東西。

我之前也感受到了這種痛苦 - 本質上,你試圖通過bar()告訴編譯器constness“傳播”。 不幸的是,據我所知,沒有辦法自動執行此操作...您只需手動編寫函數的第二個版本。

僅供參考 - OP發布的代碼是Scott Meyers的“Effective C ++ - Third Edition”中的首選方法。 見第3項。

我彎下腦子后,自己想出了答案。 但是,我想我可以使用litb的回答中的想法來改進它,我將在稍后發布。 所以到目前為止,我的解決方案如下:

class ConstOverloadAdapter {
protected:

  // methods returning pointers 

  template< 
    typename R, 
    typename T >
  R* ConstOverload(
    const R* (T::*mf)(void) const) {
      return const_cast< R* >(
        (static_cast< const T* >(this)->*mf)());
    }

  // and similar templates for methods with parameters 
  // and/or returning references or void
};

class Bar;

class Foo : public ConstOverloadAdapter {
public:
  const Bar* bar() const { 
    /* implementation */ }

  Bar* bar(void* = 0) {  // the dummy void* is only needed for msvc
                         // since it cannot distinguish method overloads
                         // based on cv-type. Not needed for gcc or comeau.
    return ConstOverload(&Foo::bar); }
};

假設你接受const-correctness作為一種技術,那么我認為這意味着你更喜歡編譯器檢查的const-correctness以簡潔。 所以你希望編譯器檢查兩件事:

  1. 當“this”為非const時,調用者可以安全地修改返回指針的referand。
  2. 當“this”是const時,無論你做什么工作都不對“this”執行任何非const操作。

如果const版本調用非const,則不會得到(2)。 如果非const版本調用const one並且const_casts結果,那么你不會得到(1)。 例如,假設Bar實際上是char ,並且您編寫的代碼最終返回(在某些情況下)字符串文字。 這將編譯(並且-Wwrite-strings不會發出警告),但是調用者最終會得到一個指向字符串文字的非const指針。 這與“您更喜歡編譯器檢查的const-correctness”相矛盾。

如果他們都調用輔助成員函數Bar *getBar() const ,那么你得到(1)和(2)。 但是如果可以編寫那個輔助函數,那么為什么你首先要搞亂const和非const版本,那么修改const Foo返回的Bar是完全可以的? 有時候,實現的一些細節可能意味着你實現了一個帶有兩個訪問器的接口,即使你只需要一個。 否則無法寫入幫助程序,或者只能由單個幫助程序替換這兩個函數。

只要代碼大小不是問題,我認為實現(1)和(2)的最佳方法是讓編譯器實際考慮兩種情況:

struct Bar { int a; };
struct Foo {
          Bar *bar()       { return getBar<Bar>(this); }
    const Bar *bar() const { return getBar<const Bar>(this); }

    Bar *bar2() const { return getBar<Bar>(this); } // doesn't compile. Good.
    Bar *bar3() const { return getBar<const Bar>(this); } // likewise

    private:
    template <typename B, typename F>
    static B *getBar(F *self) {
        // non-trivial code, which can safely call other functions with 
        // const/non-const overloads, and we don't have to manually figure out
        // whether it's safe to const_cast the result.
        return &self->myBar;
    }
    Bar myBar;
};

如果代碼是微不足道的,就像訪問對象擁有的某個數組的operator[]那么我只是復制代碼。 在某些時候,上面的函數模板比​​復制更少編碼工作,此時使用模板。

我認為const_cast方法雖然聰明且看似標准,但是沒有用,因為它選擇了簡潔性而不是編譯器檢查的const-correctness。 如果方法中的代碼很簡單,那么您可以復制它。 如果它不是微不足道的,那么對於您或代碼維護者來說,看到const_cast實際上是有效的並不容易。

你的代碼暗示const bar()實際上正在創建並返回一個新的Bar,如果你做了那么多,我發現這很奇怪。

對我來說,更大的問題是成員函數中的常量僅限於引用和非指針成員。 你擁有(非常量)指針成員的那一刻,const函數可以改變它們,即使它聲稱是盒子上的常量。

如果您的Bar實例是成員變量,請考慮返回引用。

Bar的常數是否會影響你的Foo的常數? 或者你只是這樣做,因為沒有辦法區分const Bar * vs Bar *版本? 如果是后一種情況,我只需要一個bar()返回一個Bar *並完成它。 無論如何,畢竟,需要一個const Bar *才能拿到Bar *就好了。 別忘了,bar()方法的常量是關於Foo,而不是關於Bar。

另外,如果你同時提供常量和非常量的Bar *並且很高興讓Foo成為非常量的,那么你的Foo的常量(以及bar()方法)顯然並不那么重要,你只需填寫const和非const版本就可以了。 在這種情況下,如果你允許非const版本,再次只提供/ only /那個,const的使用者將愉快地采用非const。 只是,你有const和非const去那里的方式,看起來它對程序沒有太大的影響。

如果你想讓const Foo對象能夠返回const和非const的s,那么只需要使用bar()const並返回一個非const的Bar

我不知道,我必須看到更多才能給出真正的答案。

仔細思考,哪些常量是真正連接的,以及你的程序是否需要const Foos和非const Foos。 似乎你沒有考慮到程序需要的所有可能性。

一張圖片價值超過1萬字,所以:

const Item*
Storage::SearchItemBySomething( const Something &something ) const
{
    const Item* pData = NULL;

    // Algorythm for searching.

    return pData;
}

Item*
Storage::SearchItemBySomething( const Something &something )
{
    return (Item*) ((const Storage*)this)->_SearchItemBySomething(something);
}

從實際代碼復制和更改。 一般的想法是你實現你的const方法,你編寫使用第一個方法的非const方法。 你需要將“this”指針強制轉換為“const”,你需要從返回的“const Item *”中拋棄“const”。

您可以將C樣式轉換替換為C ++樣式轉換

  • static_cast <Item *>(...)
  • const_cast <const Storage *>(this)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM