簡體   English   中英

為什么當VS沒有時,GCC在模板中需要額外的聲明?

[英]Why does GCC need extra declarations in templates when VS does not?

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行編譯時,我得到了這些錯誤:

在成員函數'void Derived :: foo()'中:
錯誤:在此范圍內未聲明't'
錯誤:“get”沒有依賴於模板參數的參數,因此必須提供“get”聲明

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

在這種情況下,GCC是正確的,並且Visual Studio錯誤地接受了格式錯誤的程序。 請查看GCC手冊中有關名稱查找的部分。 復述:

[T]他對[ get() ]的調用不依賴於模板參數(沒有依賴於類型T的參數,並且也沒有指定調用應該在[template-]依賴的上下文中) 。 因此,必須提供此類函數的全局聲明,因為基類中的函數在實例化時間之前是不可見的。

您可以通過以下三種方式解決此問題:

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

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

使用-fpermissive標志還可以讓編譯器接受代碼,方法是標記所有函數調用,在定義模板時沒有聲明可見,以便以后在實例化時查找,就像它是一個依賴調用一樣。 我們不建議使用-fpermissive來解決無效代碼,它也只會捕獲調用基類函數的情況,而不是使用基類中的變量的情況(如上例所示)。

但是我建議不要這樣做,因為手冊中提到的原因,以及你的代碼仍然是無效的C ++的原因。)

問題不在於gcc,而在於Visual Studio,它接受的代碼不符合C ++標准。

它已經在這個網站上得到了回答,所以我會簡短一些。

該標准要求模板評估兩次:

  • 一旦達到定義: template <class T> struct Foo { void bar(); }; template <class T> struct Foo { void bar(); };
  • 一旦到了實例: 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

問題 ? 任何重復使用你在VS中的模板代碼中執行的相同符號的人,如果他們的符號出現在包含模板代碼和他們實際使用模板代碼的那一刻之間,可能會讓你的實施陷入困境......不是嗎好笑:/?

現在,為您的代碼:

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

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

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

因此Derived<void>不應該編譯;)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM