2012-12-30 37 views
2

在這段代碼,爲什麼類型名T(在測試(...)函數)計算爲Foo類型,而不是類型吧?我怎樣才能改變它,使它成爲類型酒吧?爲什麼模板<typename T>評估爲富,而不是酒吧在C++

#include <typeinfo> 
using namespace std; 

struct Foo 
{ 
    virtual ~Foo() { } 
}; 

struct Bar : public Foo 
{ 
}; 

template<typename T> 
bool Test(T foo) 
{ 
    return (typeid(foo) == typeid(Bar)); 
} 

int main() 
{ 
    Bar bar; 
    Foo *foo = &bar; 
    bool THIS_IS_TRUE = (typeid(*foo) == typeid(Bar)); 
    bool WHY_ISNT_THIS = Test(*foo); 
    return 0; 
} 

回答

1

你必須保持指針或通過引用傳遞值,你不能通過拷貝(通過值傳遞)傳遞變量。

試試這個:

template<typename T> 
bool Test(T foo) 
{ 
    return (typeid(*foo) == typeid(Bar)); 
} 

int main() 
{ 
    Bar bar; 
    Foo *foo = &bar; 
    bool THIS_IS_TRUE = (typeid(*foo) == typeid(Bar)); 
    bool WHY_ISNT_THIS = Test(foo); 
    return 0; 
} 
+0

我點擊提交的錯誤...對不起,你發佈你的評論速度非常快... – benjarobin

6

模板是基於靜態類型,而不是動態分析類型推斷。該代碼根據編譯器在編譯時知道的內容進行靜態綁定和創建,與運行時的類型無關。另外,當你通過價值傳遞*foo時,無論如何你都在切割對象。

4

模板是在編譯時完成,所以他們無法弄清楚你傳遞它們的運行時類型。

無論如何,由於您傳遞的價值,你將轉換和切片到對象,這將丟失有關原始類型Bar的信息。嘗試引用,而不是:

template<typename T> 
bool Test(T& foo) 
{ 
    return (typeid(foo) == typeid(Bar)); 
} 

T仍然會被推斷爲Foo,但typeid(foo)應該Bar

+0

有什麼辦法讓T成爲酒吧,那樣我可以在foo上使用decltype並創建一個新的Bar對象? – David

+0

@David這聽起來像你想要的首先是擊敗通用代碼的點。無論如何,如果你想這樣做,只需要用'bar'調用'Test'。 – Pubby

+0

@David:當你傳遞一個靜態類型的「Foo」類型的對象時,你不會讓'T'變成'Bar'!即使你試圖強制使用它,例如'Test (* foo)',它也不會工作,因爲編譯器會拒絕將'Foo&'綁定到'Bar&'。 –

0

你想要做什麼是行不通的。這是一種替代方法,在這裏我基本上建立自己的基礎設施類型:

#include <functional> 
struct Base { 
protected: 
    virtual ~Base() {} 
    Base() {} 
public: 
    virtual size_t SizeRequired() const = 0; 
    virtual std::function<Base*(unsigned char* buffer)> Constructor() const = 0; 
    // note that standard delete is not standards compliant, because the memory 
    // was allocated as an array of unsigned char's 
    void SelfDelete() { 
    SelfDestroy(); 
    delete[] reinterpret_cast<unsigned char*>(this); 
    } 

    // you can also create these things in other storage locations: 
    void SelfDestroy() { 
    this->~Base(); 
    } 
    static void Delete(Base* b) { 
    if (b) 
     b->SelfDelete(); 
    } 
    std::function<Base*()> AllocateAndConstructor() const { 
    size_t required = this->SizeRequired(); 
    auto constructor = this->Constructor(); 
    return [required, constructor]()->Base* { 
     unsigned char* buff = new unsigned char[ required ]; 
     return constructor(buff); 
    }; 
    } 
}; 

// CRTP class (google CRTP if you are confused what I'm doing) 
template<typename Child> 
struct BaseHelper: Base { 
    static Base* Construct(unsigned char* buffer) { 
    return new(buffer) Child(); 
    } 
    static Base* Create() { 
    unsigned char* buff = new unsigned char[ sizeof(Child) ]; 
    return Construct(buff); 
    }; 
    virtual size_t SizeRequired() const { 
    return sizeof(Child); 
    } 
    virtual std::function<Base*(unsigned char* buffer)> Constructor() const { 
    return &BaseHelper<Child>::Construct; 
    } 
}; 

// use: 

struct Bar: BaseHelper<Bar> { 
}; 
struct Foo: BaseHelper<Foo> { 
}; 

Base* test(Base* b) { 
    auto creator = b->AllocateAndConstructor(); 
    return creator(); 
} 
#include <iostream> 
int main() { 
    Base* b = Bar::Create(); // creates a Bar 
    Base* b2 = test(b); // creates another Bar, because b is a Bar 
    Base* f = Foo::Create(); // creates a Foo 
    Base* f2 = test(f); // creates another Foo, because f is a Foo 
    std::cout << (typeid(*b) == typeid(*b2)) << " == 1\n"; 
    std::cout << (typeid(*f) == typeid(*f2)) << " == 1\n"; 
    std::cout << (typeid(*f) == typeid(*b)) << " == 0\n"; 
    Base::Delete(b); 
    Base::Delete(b2); 
    Base::Delete(f); 
    Base::Delete(f2); 
    Base::Delete(0); // does not crash 
}; 

這意味着一個基本的每次運行時實例與它進行訪問,構建基地的實例的能力。重寫新的和刪除可能比使用上面的替換創建和刪除操作更好,並且可以讓某人在堆棧上創建這些東西。

我建議對這種技術的,除非你知道自己在做什麼。

有,但是,相對常見的模式,如克隆和工廠,是接近上述但有點不太傻。

相關問題