2012-11-02 79 views
3

這個問題需要C++模板元編程的知識,因爲涉及(間接)表達式模板。我間接地說,因爲它不是直接表達模板的問題,而是涉及C++類型的計算。如果你不知道那是什麼,請不要回答這個問題。讓編譯器做出最終選擇使用哪種類型

避免把出了一道題沒有足夠的背景資料,讓我闡述我試圖解決,然後去到更具體的部位一般問題一點。

假設您有一個圖書館,提供Integer s,用戶可以像使用int s一樣進行計算。 此外,可以從int構建Integer。就像:

Integer<int> i(2); 

內部我Integer類是類模板:

template<class T> 
class Integer { 
    // cut out 
}; 

因此,我可以把它定義在任何整數類型,我喜歡。

現在不改變API,我想更改庫的方式,如果Integer是從一個int構造應該通過內部不同類型的代表,說IntegerLit。這樣做的原因是,我可以加快一些計算知道的Integer一個實例是從一個int創建(可將它作爲int參數的函數,而不是如由基指針+單獨的數據。這裏所描述的一般目的只是作爲一個評論。)

重要的是,從int施工時,因爲我需要編譯器佔用不同的代碼路徑取決於是否從int或並非建造的類型是不同的。我不能用運行時數據標誌來做到這一點。 (原因總之:編譯器生成一個函數,可以使用int或上面提到的更一般類型的對象,具體取決於類型。)

說到這裏,我遇到了一個問題:當使用類似於這樣的:

Integer<int> a,b(2); 

a = b + b; 

這裏a應該是一般Integerb專業IntegerLit。但是,我的問題是如何在C++中表達這一點,因爲用戶可以自由地使用非常相同的類型Integer來定義她的變量。

使類型的多態,即派生IntegerLitInteger將無法​​正常工作。它看起來好一會兒。然而,由於用戶創建了Integer(基類)的實例,因爲它是編譯器粘貼到表達式樹中的基類(這就是表達式模板涉及到問題的原因),所以它不會工作。所以在這兩種情況之間再也沒有區別。做一個RTTI檢查動態演員陣容真的不是我想要的那一點。

更有希望的是,似乎將字面模板參數bool lit添加到說明是否由int構建的類型。重點是不指定一個非文字轉換規則,而是爲其他情況指定轉換規則。

但是,我無法得到這個工作。如果不是從int構建,以下代碼僅編譯(GCC 4.7 C++ 11)。否則,它會失敗,因爲未將整數指定爲true作爲​​3210的值。所以編譯器搜索沒有轉換規則的默認實現。這不是更改API的選項,並且在從int構建時需要編寫Integer<int,true>

template<class T,bool lit=false> 
class Integer 
{ 
public: 
    Integer() { 
    std::cout << __PRETTY_FUNCTION__ << "\n"; 
    } 
    T F; 
}; 

template<> 
template<class T> 
class Integer<T,true> 
{ 
public: 
    Integer(int i) { 
    std::cout << __PRETTY_FUNCTION__ << "\n"; 
    } 
    T F; 
}; 

我開始想知道這樣的事情是否可以用C++。

C++ 11的新功能可以幫助嗎?

+0

您的代碼通常無效,因爲'Integer'是一個類模板,需要一個模板參數來創建它的一個對象。只是'整數a(2);'是一個錯誤。 – Xeo

+0

對,我在這裏簡化了。實際上,用戶使用'typedef'-ed類型,例如專門用'Integer'和'int'。 – ritter

+1

你的意思是從'int'構造還是從整數文字構造?有一個重大的區別。 –

回答

5

不,這不是C++的工作原理。如果您將b定義爲Integer b,那麼它是Integer。無論隨後用於初始化b的表達式如何,都適用。

另外,請考慮以下內容:extern Integer b;。有一個表達式在其他地方初始化了b,但編譯器仍然需要弄清楚b有哪些類型。 (不是「將會有」,而是「有」)。

+0

'extern'是一個很好的觀點。因此,在C++中,編譯器使用不同類型的依賴於哪個構造函數是不可能的。那是你說的嗎? – ritter

+0

@Frank:沒錯,因爲可用的構造函數依賴於變量的類型。 – Xeo

+0

@Xeo:這是另一個好點。你需要做重載解析來選擇ctor,但是這假設你已經在類Integer中解決了。哎呀,在解析'Integer b(5)'這一行時,你需要弄清楚'b'是一個返回Integer的函數還是一個Integer類型的新變量。這對於確定'Integer'部分的編譯器已經足夠複雜了。 – MSalters

3

你不能這麼做,完全是反正。 但使用「自動」,你可以靠近。

// The default case 
Integer<int> MakeInt() 
{ 
    return Integer<int>(); 
} 

// The generic case 
template <class T> 
Integer<T> MakeInt(T n) 
{ 
    return Integer<T>(n); 
} 

// And specific to "int" 
Integer<IntegerLit> MakeInt(int n) 
{ 
    return Integer<IntegerLit>(n); 
} 

auto a = MakeInt(); 
auto b = MakeInt(2); 
auto c = MakeInt('c'); 
auto d = MakeInt(2.5); 
+0

+1,雖然承認不太方便。 –

1

你不能那樣做。一旦你說變量是Integer<int>這是變量的類型。

template<class T> 
class Integer { 
    // cut out 
    Integer() : rep_(IntRep()) {} 
    explicit Integer(int val) : rep_(IntLitRep(val)) {} 

private: 
    boost::variant<IntRep, IntLitRep> rep_; 
}; 

然後你就可以輕鬆地確定哪些變種版本是活動的,並利用不同的代碼路徑需要的時候:你可以做的是使Integer的根本代表取決於使用的構造,像這樣什麼。

編輯:在這種情況下,即使Integer的類型是相同的,你可以很容易地使用模板函數使它看起來像兩個獨立的類型(因爲rep更改了有效類型)。

+0

好的,但這對我的情況沒有幫助。你說過:我用C++不可能做到的。 – ritter

相關問題