2012-12-21 80 views
2

我剛剛開始使用C++模板,僅僅是因爲我想了解與其他語言(Java)的具體差異,並且我達到了一個他們開始發散的點,但我不是得到我應該如何解決具體問題(或解決它)。C++模板 - 具有模板類型的泛型方法

假設我有一個通用的值類,如

template <class T> 
class Value 
{ 
    protected: 
    T value; 

    public: 
    Value(Type type, T value) : type(type), value(value) {} 

    void set(T value) { this->value = value; } 
    T get() const { return this->value; } 
    T clone() { return new Value<T>(type, value); } 

    virtual string svalue() const = 0; 

    const Type type; 
}; 

和特定亞型:

class Int : public Value<int> 
{ 
    public: 
    Int(int value) : Value<int>(INT, value) { }; 

    virtual string svalue() const { ... } 

    friend ostream& operator<<(ostream& os, const Int& v); 
}; 

(我知道這是也可以使用template <>但因爲我指定類型的特定代碼仍然需要使用它足以理解它,我剛剛由自己的Int類定義,這不過是一個typedef Value<int>到底)

是否有可能讓我們說可以存儲任意指向Value實例的指針的集合?不需要指定泛型類的具體具體類型。

從我所瞭解的模板只是一個編譯時問題,編譯器會分析所有使用該模板的具體類型,併爲它們中的每一個編譯相同方法的不同版本,因此我試圖似乎不可能(在Java中,我允許使用通配符來表示List<Value<?>>)。我錯了嗎?

是否有一個常見的設計來解決這個問題,或者我被迫放棄模板來實現它?

+0

是,有'價值'從一個普通的類型繼承。 – andre

回答

3
#include <iostream> 
#include <memory> 

class Base 
{ 
    public: virtual void Print() = 0; 
}; 

template<typename T> 
class Derived : public Base 
{ 
    T V; 
public: 
    void Print() { std::cout << V; } 
    Derived(T v) : V(v) { } 
}; 

int main() 
{ 
    std::unique_ptr<Base> Ptr (new Derived<int>(5)); 
    Ptr->Print(); 

    return 0; 
} 

我認爲這是不言自明的。

+0

@FredOverflow我秒。 – johnathon

+0

確實。對我感到羞恥。 –

2

是否有可能有,比方說,一個集合,是能夠存儲 任意指針的值時?

不,不是你想要的樣子。這是不可能的:

template <class T> 
class Value 
{ 
// ... 
}; 

vector<Value> my_values_; 

這是不可能的,因爲Value不是一個類型 - 它真的只是一個藍圖,想法,如果你願意。拋開不合邏輯的思考,你不能存儲想法,你只能存儲事物。 A Value不是一回事。

如果這是你想要的,那麼模板可能是該作業的錯誤工具。您可能真正需要的小麥是抽象基類,其中基類(例如class Value)定義了界面,子類(例如class Int : public Value)定義了具體的類型。這樣,你可以創建通用Value S的容器,使用指針:

vector<Value*> my_values_; 

或者,更好的使用智能指針:

vector<unique_ptr<Value>> my_values_; 
0

是否有可能有,比方說,一個集合那是否能夠將 任意指針存儲到值實例?

不,它不會工作。然而,至少有更多的可能:

  1. 如果你事先知道你要在列表中使用每一種類型,您可以使用boost::variant

  2. 您可以將指針列表對象(實際void*或你可以刪除模板並使其作爲基類)並以某種方式(例如dynamic_cast)將它們轉換爲某些特定的對象。

+0

如果實際上需要dynamic_cast,則不應將所有對象存儲在一個容器中,因爲它的要點是什麼? –

1

Java技術可以通過混合使用通用基類(參見Bartek的其他答案)和類型擦除技術在C++中完成。

其中值實際上是值的C++版本無法在Java中完成。如果我記得正確的話,它可以用一些編譯爲Java字節碼的語言完成。

在Java中,您可以獲得的唯一對象實際上更像是垃圾收集指向C++中對象的指針。直接存儲或引用的實際對象的實例是verbotin,因爲這會妨礙Java風格垃圾回收。

因此,Java中Value<?>的容器類似於在C++中垃圾收集的所有類型的公共基類的指針容器。訪問每個實例然後涉及在Java中等效的dynamic_caststatic_cast

對於更多的Java esque行爲,給Value賦予與虛擬平凡析構函數相同的基礎,在所有實例上具有相同簽名的純虛擬通用方法,實現具有不同簽名的模板版本以及產生shared_ptr的工廠函數s到Value實例。

shared_ptr的容器用於Value基,並在需要時使用動態共享ptr轉換來獲取特定接口。

現在所有這些意味着您的代碼比沒有所有結構的代碼慢10到100倍,但它可能仍然比等效的Java版本更快。如果你不需要它,你可以選擇不使用它。

1

我總是喜歡混淆事物,並拋出一個很好的語法扭曲,雖然它仍然是一樣的(使用共同的基類)。唯一的奇數位是該基類的Value<T>拼寫Value<>,並且可以在一個容器中被用作這樣的(雖然不是直接的,當然,由於需要使用一個點,以避免限幅):

#include <memory> 
#include <vector> 

template <typename T = void> 
class Value; 

template <> 
class Value<void> 
{ 
public: 
    virtual ~Value() {} 
}; 

template <typename T> 
class Value 
    : public Value<> 
{ 
    T value_; 
public: 
    Value(T value): value_(value) {} 
    // whatever 
}; 

template <typename T> 
std::unique_ptr<Value<T>> make_value(T value) { 
    return std::unique_ptr<Value<T>>(new Value<T>(value)); 
} 

int main() 
{ 
    std::vector<std::unique_ptr<Value<>>> values; 
    values.push_back(make_value(0)); 
    values.push_back(make_value(0.0)); 
    values.push_back(make_value(false)); 
}