2016-11-29 74 views
2

我有一個模板Base我想要繼承的類。 Base類有一個公共方法,它對模板類型進行了假設。代碼的簡化版本,介紹如下:在C++中繼承不支持的模板類

#include <iostream> 

using namespace std; 

template <typename V> 
class Base { 
public: 
    virtual void print() { 
    a = "a"; 
    cout << "Base class" ; 
    } 
    void callIt() { 
    print(); 
    } 
    V a; 
}; 

class Derived : public Base<int> { 
public: 
    void print() override { 
    cout << "Derived class\n"; 
    } 
}; 


int main() { 
    Derived d; 
    d.callIt(); 
    return 0; 
} 

編譯此代碼與GCC 6.2.0給了我這個錯誤:

test.cpp: In instantiation of 'void Base<V>::print() [with V = int]': 
test.cpp:13:10: required from 'void Base<V>::callIt() [with V = int]' 
test.cpp:28:12: required from here 
test.cpp:9:7: error: invalid conversion from 'const char*' to 'int' [-fpermissive] 
    a = "a"; 
    ~~^~~~~ 

我想完全隱藏Base類實現print()方法來自編譯器。我怎樣才能做到這一點?

編輯︰諷刺的是,註釋行a = "a";允許代碼編譯成功,並將打印出Derived class

+0

對不起,爲什麼'Base :: print'會假設'V'是否'Base'被更普遍的模板所繼承? – Brian

+0

@Brian'Base'是一個庫的一部分,我無法控制它,但我想要它提供的其他功能。 – Javad

+1

你知道什麼真的很有趣嗎?註釋掉'a =「a」;'並查看輸出。 http://melpon.org/wandbox/permlink/Kci1HFRYtKbtbLk8 (擾流警報:輸出是'Derived class'。什麼?) –

回答

2

由於您的print是虛擬的,因此無論何時從Base繼承(因爲編譯器需要生成正確的vtable),即使該函數尚未調用,也必須實例化它。如果print是非虛擬成員,情況就不會如此 - 在這種情況下,只有在被調用時纔會被實例化。

我能想到的唯一解決方案就是看看你是否可以擺脫這裏的虛擬功能 - 如果你不能,對不起,你運氣不好。

+0

你是對的,但我認爲我已經簡化了代碼。我只是更新了代碼。 'print()'正在被'Base :: callIt()'調用。在舊代碼中刪除虛擬會有所幫助,但在新代碼中則不會。我試着註釋掉'a =「a」;'並編譯了代碼,得到了'Base class'而不是'Derived class'。任何解決這個問題的方法? – Javad

4

,如果你宣佈Derived之前Base<int>::print()添加專業化有可能讓你的代碼工作:

#include <iostream> 

using namespace std; 

template <typename V> 
class Base { 
public: 
    virtual void print() { 
    a = "a"; 
    cout << "Base class" ; 
    } 
    void callIt() { 
    print(); 
    } 
    V a; 
}; 

template <> 
void Base<int>::print() { 
    cout << "Specialization\n"; 
} 

class Derived : public Base<int> { 
public: 
    void print() override { 
    cout << "Derived class\n"; 
    } 
}; 


int main() { 
    Derived d; 
    d.callIt(); 
    return 0; 
} 

Live Demo

不過要小心這樣的:它打破了Base筆者對該假設哪些類型與模板一起工作,並且很容易導致庫代碼中難以追蹤的錯誤。

+0

爲什麼需要'Base :: callIt()'? @Javad無法將其添加到'Base',並調用'd.print()'可以正常工作:http://melpon.org/wandbox/permlink/7bFxmV88g1izYBbc –

+1

因爲它在OP的代碼中。 'Base :: callIt()'是OP不能從'Base :: print()'中刪除虛擬限定符的原因。 –

+0

哦,對不起,我沒有看到。但公平起見,在將代碼複製到Wandbox後,它被編輯到原始文章中:) –

0

簡化隔離問題:

template <typename V> 
class Base { 
public: 
    virtual void print() { 
    a = "a"; 
    } 

    V a; 
}; 

int main() 
{ 
    Base<int> b; 
} 

結果:

<source>:5:7: error: invalid conversion from 'const char*' to 'int' [-fpermissive] 
a = "a"; 

這是因爲我們實際上是說:

int a = "a"; 

,當然這是一個錯誤,因爲您不能將const char*分配給int

因此,這裏得出的唯一合理結論是Base<T>的設計約束是必須存在從const char*T的可訪問轉換。

如果你想要T是其他任何東西,那麼這個基類是不適合的,你需要找到另一種方法來實現你想要的(封裝?)。