2010-05-11 142 views
16
template<typename T> 
class Base 
{ 
protected: 
    Base() {} 
    T& get() { return t; } 
    T t; 
}; 

template<typename T> 
class Derived : public Base<T> 
{ 
public: 
    Base<T>::get;     // Line A 
    Base<T>::t;      // Line B 
    void foo() { t = 4; get(); } 
}; 

int main() { return 0; } 

如果我註釋掉線A和B,該代碼的Visual Studio 2008下編譯罰款然而,當我GCC 4.1線A和B評論下編譯,我得到這些錯誤:爲什麼GCC在模板中需要額外的聲明?

In member function ‘void Derived::foo()’:
error: ‘t’ was not declared in this scope
error: there are no arguments to ‘get’ that depend on a template parameter, so a declaration of ‘get’ must be available

爲什麼一個編譯器需要A行和B行,而另一個不行?有沒有辦法簡化這個?換句話說,如果派生類使用基類中的20個東西,我必須爲從Base派生的每個類放置20行聲明!有沒有解決這個問題的方法,不需要太多的聲明?

+6

的強制性鏈接到C++ FAQ:http://www.parashift.com/c++-faq- lite/templates.html#faq-35.19 – UncleBens 2010-05-11 16:31:57

+4

簡短回答:因爲gcc符合標準,而且(令人驚訝的)Visual C++不是? 「錯誤地」是 – 2010-05-11 16:57:20

回答

15

GCC在這種情況下是正確的,並且Visual Studio錯誤地接受了格式錯誤的程序。查看GCC手冊中關於Name lookup的部分。釋義:

[T]he call to [ get() ] is not dependent on template arguments (there are no arguments that depend on the type T, and it is also not otherwise specified that the call should be in a [template-]dependent context). Thus a global declaration of such a function must be available, since the one in the base class is not visible until instantiation time.

可以解決這個問題在任何的三種方式:

  • 您正在使用的聲明。
  • Base<T>::get()
  • this->get()

(還有第四個方法,如果你想屈服於黑暗面:

Using the -fpermissive flag will also let the compiler accept the code, by marking all function calls for which no declaration is visible at the time of definition of the template for later lookup at instantiation time, as if it were a dependent call. We do not recommend using -fpermissive to work around invalid code, and it will also only catch cases where functions in base classes are called, not where variables in base classes are used (as in the example above).

但是我不推薦的是,無論對於原因因爲你的代碼仍然是無效的C++。)

+0

不正確。如果您在Visual C++中禁用非標準擴展(您應該這樣做,如果要使用它來編寫可移植代碼),它會警告您您的代碼使用此擴展。 – 2010-05-11 22:43:16

+1

不,這些擴展比低級的「標準」更好。 – HardCoder 2012-02-28 10:02:00

5

這個問題不是用gcc,而是用Visual Studio,w它接受不符合C++標準的代碼。

它已經在這個網站上得到了回答,所以我會簡短介紹一下。在定義點

  • 一次:template <class T> struct Foo { void bar(); };
  • 在instanciation點
  • 一次:

    標準要求的模板進行兩次評估Foo<int> myFoo;

第一次,所有的非從屬名稱應從上下文中扣除:

  • 編譯器將拋出,如果你忘記了標點符號,是指未知類型/方法/屬性
  • 編譯器會選擇參與在這一點上的功能超載

因爲C++的語法是不明確的,它是必要的在此階段幫助解析器,並適當使用templatetypename關鍵字來手動消除歧義。

不幸的是,Visual Studio不符合規範,只實現第二次評估(在實例化時)。對於懶人的好處是,你可以擺脫沒有這些額外的templatetypename關鍵字,缺點是你的代碼是非法的構造,而不是便攜式...

現在最有趣的部分:

void foo(int) { std::cout << "int" << std::endl; } 

template <class T> void tfoo(T i) { foo(i); } 

void foo(double) { std::cout << "double" << std::endl; } 

int main(int argc, char* argv[]) 
{ 
    double myDouble = 0.0; 
    tfoo(myDouble); 
    return 0; 
} 

用gcc編譯,輸出int

用Visual Studio編譯,它輸出double

這個問題?任何人在你的模板代碼中重複使用相同的符號可能會導致你的實現混亂,如果它們的符號出現在包含模板代碼和實際使用模板代碼的那一刻之間......是不是有趣:/?現在

,爲您的代碼:

template<typename T> 
class Derived : public Base<T> 
{ 
public: 
    void foo() { this->t = 4; this->get(); } 
}; 

this表明,以下名稱是從屬名稱,即它取決於T(當符號單獨出現這不是很明顯)。因此,編譯器將等待實例化,並查看是否爲實例化模板Base<T>確實包含這些方法。這不是強制性的,因爲我可以完全專注Base

// It's non-sensical to instanciate a void value, 
template <> 
class Base<void> {}; 

並由此Derived<void>不應該編譯)

相關問題