2016-07-29 19 views
0

我想添加編譯時檢查double的不同含義。在現實世界中,我試圖確保所有計算都以一致的單位完成。爲了這個問題的目的,我編制了一個玩具的例子,其中數字有味道。如何防止模板化別名的隱式保留值轉換?

我一直在嘗試基於模板參數來實現此目的。使用C++ 0x中的別名功能在another answer描述,我宣佈一個Number<Flavor>爲:

enum Flavor { Cherry, Plum, Raspberry }; 

template <Flavor> using Number = double; 

這讓我聲明局部變量或參數數目的特定口味,然後使用這些變量作爲普通雙打的能力大多數情況下。

我的問題是,我無法找到一個方法來聲明一個函數,只接受特定的香味作爲它的參數:

void printCherryNumber(Number<Cherry> num) { cout << num << endl; } 

int main() { 
    Number<Cherry> a(5); 
    Number<Plum> b(6); 
    Number<Raspberry> c(3.1415); 

    printCherryNumber(a); 
    printCherryNumber(b); // O, if only this could be a compiler error. 

    return 0; 
} 

我的目標是讓printCherryNumber(b)無法編譯,因爲bNumber<Plum>不是Number<Cherry>。許多現有的問題解決方案似乎不適用於我用於Number的類型別名構造。

的東西,我已經試過

this answer,我看到的建議,添加明確不做任何事或斷功能的模板化版本,如

template <typename T> void printCherryNumber(T num) = delete; 

,沒有任何效果可言,爲什麼它呢? Number<Plum>確實是doubleNumber<Cherry>也是double所以編譯器從不打擾模板版本。

Another answer建議使用一個模板函數,靜態斷言,如:

template <Flavor F> void printPlumNumber(Number<F> num) { 
    static_assert(F == Plum, "Wrong number flavor!"); 
    cout << num << endl; 
} 

失敗的原因是無論F的實際價值,Number<F>仍然只是double,所以我得到一個錯誤有關不能夠推斷出F的值。

在別處有人suggests explicit specialization,這也沒有這種情況:

template <Flavor F> void printRaspberryNumber(Number<F> num) = delete; 

template <> void printRaspberryNumber<Raspberry>(Number<Raspberry> num) { 
    cout << num << endl; 
} 

這裏,編譯器將調用模棱兩可,部分也是因爲它不能爲F推斷值。

大象在房間

我當然可以,讓Number

template <Flavor> struct Number { double value; }; 

形式的單個價值結構,但我想避免這個選項,因爲我不是在我的代碼中到處都有.value的想法讓我非常興奮,我也不是特別渴望定義運營商爲Number,只是代理服務器翻倍。

強制性ideone

http://ideone.com/4HiYtI

回答

2

這種方法的問題:

enum Flavor { Cherry, Plum, Raspberry }; 

template <Flavor> using Number = double; 

是別名模板是透明的。 Number<Cherry>,Number<Plum>double都是相同的類型。這根本不能解決你的問題。


你想要的是通常稱爲不透明的typedef。你真的想你的最後一個選項:

template <Flavor> 
struct Number { 
    double value; 

    operator double() const { return value; } // for convenience 
    Number& operator=(double); // if necessary 
    // possibly more operations 
}; 

這樣,Number<Cherry>Number<Plum>不同類型。他們是而不是可以相互轉換。並且double不能隱式轉換爲其中之一。


你也可以看看BOOST_STRONG_TYPEDEF及其實現,它的目的也是爲了解決這個問題。

1

您試圖避免的選項是真正實現此目的的唯一方法。

模板別名就是它:別名。模板別名等同於基礎類型。在各方面。

template <Flavor> using Number = double; 

這意味着Number<Flavor>double。這不是別的。 Number<Plum>也是double。這與全球搜索/將其中任何一個替換爲double幾乎相同。最終結果將是相同的。類型完全相同。

您只能「聲明一個只接受」特定類型的函數。除了使用模板別名外,模板別名是同一類型,因此不可能聲明接受double但不接受double的函數。這是合乎邏輯的錯誤。

struct中包裝double是實現嚴格類型檢查的唯一方法。它沒有那麼壞。折騰一些重載,幾個operator s,你的包裝struct將執行嚴格的類型檢查,編譯器可能會產生相同的代碼,而不會造成運行時間損失。