2012-09-10 60 views
1

我想寫一些字符串包裝,將接受一個字符串,如果它是有效的將其類型:重構到模板

  • Length有效字符串:毫米,男,英尺,在
  • Angle有效字符串:度,弧度

我想象一個使用,如:

Length len = read_from_keyboard(); // or some means of initialization 
if(len.is_valid()) { ... } 

所以我寫了這些實現。

struct Length 
{ 
    QString m; 

    Length() {} 

    Length(QString s) { if(is_valid_string(s)) { m = s; }  } 

    QString operator()() { return m;  } 

    bool is_valid()  { return is_valid_string(m);  } 

    static bool is_valid_string(QString s) { 
    return s == "mm" || s=="m" || s=="ft" || s=="in"; 
    } 
}; 

struct Angle{ 
    QString m; 

    Angle() {} 

    Angle(QString s) { if(is_valid_string(s)) { m = s; }  } 

    QString operator()() { return m;  } 

    bool is_valid()  { return is_valid_string(m);  } 

    static bool is_valid_string(QString s) { 
    return s == "deg" || s=="rad"; 
    } 
}; 

這在我看來,某種形式的靜態多態性,以is_valid_string()他們在執行唯一的區別。

由於我有一些這些類,我想使用靜態繼承(而不是通過虛擬)來掌握常用功能。

於是,我想到了使用奇異遞歸模板模式的:

template <class T> 
struct ConstrainedText { 
    QString m; 

    ConstrainedText() {} 

    ConstrainedText(QString s) { if(T::is_valid_string(s)) { m = s; } } 

    QString operator()()  { return m;  } 

    bool is_valid()    { return T::is_valid_string(m); } 

}; 


struct Angle : public ConstrainedText<Angle> { 
    static bool is_valid_string(QString s) { 
     return s == "deg" || s="rad"; 
    } 
}; 


struct Length : public ConstrainedText<Angle> { 
    static bool is_valid_string(QString s) { 
     return s == "mm" || s="m" || s=="ft" || s=="in"; 
    } 
}; 

但現在我已經失去了基類中隱含的構造,我不得不重寫他們!

有沒有我可以以具有相同的接口[default constructorimplicit constructoris_value()],只有實現這個任何其他方式寫的不同部分(靜態is_valid_string())最少的代碼?

我知道我可以使用預處理器,但我希望代碼對調試器友好。

+0

我的問題是不是真的如何繼承的構造函數,但如何寫一個重構,這樣我只需要實現每個子類中is_valid_string()函數。 –

回答

1

正如其他人已經指出,由於構造函數沒有繼承,你將不得不重新定義它們。但是你可以做這樣的事情,code at ideone.com

#include <string> 
#include <stdexcept> 
#include <iostream> 

template <class T>   
class ConstrainedText {   
    std::string m;     

protected: 
    ConstrainedText() {} 
    ~ConstrainedText() {} 
public: 
    bool is_valid() {   
    return T::is_valid_string(m);   
    }   

    static T Create(std::string const & s) 
    { 
     if (T::is_valid_string(s)) { 
      T t; 
      static_cast<ConstrainedText<T>&>(t).m = s; 
      return t; 
     } 

     throw std::runtime_error("invalid input!"); 
    } 
};   

struct Angle : public ConstrainedText<Angle> {   
    static bool is_valid_string(std::string s) {   
     return s == "deg" || s=="rad";   
    }   
};   


struct Length : public ConstrainedText<Length> {   
    static bool is_valid_string(std::string s) {   
     return s == "mm" || s == "m" || s == "ft" || s == "in";   
    }   
};   

int main() 
{ 
    auto a = Angle::Create("deg"); 
    auto l = Length::Create("mm"); 

    try { 
     Angle::Create("bimbo"); 
    } catch (std::runtime_error & pEx) { 
     std::cout << "exception as expected" << std::endl; 
    } 

    try { 
     Length::Create("bimbo"); 
    } catch (std::runtime_error & pEx) { 
     std::cout << "exception as expected" << std::endl; 
    } 
} 
1

構造函數不是繼承的,你不能繼承它們的using

在C++ 11可以使用可變參數模板和完美轉發:

template<typename... Args> Derived(Args &&...args): 
    Base(std::forward<Args>(args)...) {} 
0

由於構造函數永遠不會用C++繼承(默認以外,如果沒有構造函數定義),你不得不擁有一個實現他們再次自己或模板化(帶或不帶C++ 11可變參數模板)以使編譯器與構造函數匹配。但請注意,這樣做會使文檔和編譯器錯誤更難解釋,因爲無意義的模板構造函數會添加(有時)令人困惑的抽象層。

預處理器操作符也可以解決這個問題,但我只會這樣做,如果構造函數非常簡單並且容易跨所有繼承器複製。

因此,一般來說,如果您可以自動清潔地生成繼承的默認構造函數和隱式構造函數,那麼答案是否定的。但是,上述兩種方法會以一種凌亂但自動的方式進行。

+0

如果我們不得不重新考慮使用的CRTP,是否還有另一種方法來編寫代碼,以便只爲更改的函數編寫代碼? –

+1

@GrimFandango說實話,除非你打算有很多這些定義,我只是寫出每個的構造函數。這不是很多工作和簡單的死亡。否則,我會說你可能有一個預處理器命令來設置整個構造器集或集的一部分的一個很好的候選案例。另外作爲一個附註,你在你的is_valid檢查中有一些拼寫錯誤 - s =「m」不是你的意思,s ==「m」也不是。看看string :: compare。 – Pyrce

+0

你對's =「m」'是正確的,但's ==「m」'起作用,因爲s是一個QString,「m」通過隱式轉換器QString –

0

我用政策並化險爲夷。現在我不必重寫構造函數,我只需要實現is_valid_string()。它現在也符合C++ 98標準。

template <class Policy> 
struct ConstrainedText { 
    QString m; 

    ConstrainedText() {} 

    ConstrainedText(QString s) { if(Policy::is_valid_string(s)) { m = s; } } 

    QString operator()()  { return m;  } 

    bool is_valid()    { return Policy::is_valid_string(m); } 

}; 


struct Angle_policy { 
    static bool is_valid_string(QString s) { 
     return s == "deg" || s="rad"; 
    } 
}; 

struct Length_policy { 
    static bool is_valid_string(QString s) { 
     return s == "mm" || s="m" || s=="ft" || s=="in"; 
    } 
}; 

typedef ConstrainedText<Length_policy> Length; 
typedef ConstrainedText<Angle_policy> Angle; 

謝謝大家的幫助