差異

2017-08-09 28 views
-4

所以我最近遇到了MSVC一個問題,即它不會編譯我的代碼,根據MSVC開發者社區這一行是罪魁禍首(在頭引起編譯錯誤一樣<cmath><type_traits>差異

#define type typename //because type T looks infinitely better than typename T 

的(應該)修正爲:

#define type typename1 

問題是我無法找到「typename1」,什麼是它們之間的區別,爲什麼typename1不會導致數百錯誤,犯錯的任何文件當typename呢?爲typename定義別名只是一種不好的做法,我無法想象它會如何,但也許呢?

因此,要回到問題,typename和typename1如何不同,爲什麼此代碼打破?

#define type typename //this is supposedly the problem 

#include <cstdint> 
#include <cstdio> 
#include <cmath> //this breaks 
#include <type_traits> //this breaks 
//as would any other header that includes corecrt_math or similar 

//to reproduce the problem we need not include templates, 
//but there would naturally be several since I want to use the 'type' 
//identifier 

int main() 

{ 

    return 0; 

} 
+4

'typename'是關鍵字。讓它出現在不應該會導致錯誤的地方。這是宏應該避免的原因的一部分。 – NathanOliver

+2

[typename](http://en.cppreference.com/w/cpp/keyword/typename)是C++中的保留關鍵字。 _typename1_不是。將'const'或'constexpr'選爲'define'。 – Ron

+1

在'C++'中,你可能不想使用#define。 – drescherjm

回答

1

TL; DR:您正嘗試通過宏替換獲取類型別名。這總是有風險的。解決方案是替換宏with a using declaration。例如

using myBigInteger = long long int; 

一般形式:

using <alias> = <data type> 

更復雜的例子:

using datavector = std::vector<int32_t>; // a vector of 32 bit ints 
using dataiterator = datavector::iterator; // and iterator of a vector of 32 bit ints 
using functionpointer = void (*)(dataiterator begin, dataiterator end); 
// a pointer to a function that does something to the specified iterator range 

上面給出了良好的一站到位改變的潛在數千個位置的類型中的代碼其中std::vector<int32_t>需要更改爲std::vector<double>,但是一個簡單的查找和替換將不會執行,因爲只有一些std::vector<int32_t> s,充滿數據的,需要更改。

下不使用typename

using type = typename; 

這會失敗,就像嚴重,但有所不同,因爲類型名稱是依賴於上下文特殊含義的保留期限。通常你會希望沿着

error: ‘typename’ does not name a type

的線,但在這種情況下,類型名稱是用來declare dependent, nested, naming東西,所以可憐的編譯器去尋找這種嵌套並不能找到它。喜劇的錯誤消息可能會隨之而來。

龍版,爲什麼給定的代碼示例失敗:

首先,一點有用的閱讀:編譯過程開始How does the compilation/linking process work?

之前,C++預處理器(爲此事C預處理器)讓一個C++源文件的大量文件及其包含的所有頭文件。與此同時,處理器將執行替代已定義的令牌的文本替換。這意味着每次找到令牌type時,它都將被令牌typename替換。不要緊,哪個文件包含type

#define type typename 

被發現。 type的所有實例都將被替換爲您的文件,標準庫頭文件,第三方頭文件,因爲它現在全部是一個大文件。

在預處理程序完成收集並轉換所需的所有內容後,編譯器會在這個大文件上運行。在這種情況下,它會找到保留字typename,這些保留字通過文件分散在絕對沒有意義的位置,並相應地排出錯誤消息。在這種情況下,雖然typename不好,但實際上任何名稱都不好,因爲文本替換完全沒有意識。它並不關心令牌如何被使用。令牌被替換。

#define type fubar 

將重命名標識符typefubar這可能與已命名fubar其他標識符衝突。代碼的讀者將不知道發生了什麼。我曾經用一個名爲strlen的變量在腳下自殺,因爲strlen庫函數是作爲宏實現的。浪費的時間通過導致的bizarro錯誤信息除草。

請注意,只要將#define移至包含所有標題後,您可能會發現一定程度的成功,但非法使用typename仍然是一個跳閘點。

+0

在g ++下,它使用包含最後一個配置頭文件(它將#define type typename指令放在最後)來構建,但是在MSVC下它並沒有這麼做,這就是爲什麼我質疑microsoft的解決方案用Microsoft擴展名替換標準指令的原因。 好吧不是現在很重要,我已經通過regex 使用name = otherName的黑魔法重構了整個代碼庫可能適用於我的其他宏,如#define Euler vector3 所以無論如何我會獲得從這整個崩潰的東西。 – LordHexahedron

+0

在'typedef'(或更近期的'使用')的地方使用'#define'已經有二十多年的糟糕做法 - 實際上沒有理由一開始就這樣做。 – Useless

2

Is it just bad practice to define an alias for typename, I can't imagine how it would be but maybe?

是的,這是不好的做法,重新定義的宏語言的位。

Here是對關鍵字typename的描述。它是標準的,定義明確的,並且在任何地方都能使用。每個人都知道它意味着什麼,包括語法高亮編輯器和其他工具,而無需運行預處理器。現在考慮令牌type。我可以使用它的動詞形式作爲函數名稱。我可以使用它作爲變量名稱。我甚至可以將它用作類型的名稱。我可以做所有這些事情,因爲它不是關鍵字

讓預處理器替換所有這些變量,功能和類型與關鍵字將打破東西。 C(++)預處理器是定製語言的非常非常鈍的工具,因爲它完全不瞭解上下文。

在任何情況下,試圖使C++看起來像另一種語言很少結束。語法不夠靈活,而且宏不那麼強大。

斷言type T看起來比typename T看起來好得多弱。如果無限更好真的意味着無限地更喜歡一些其他的東西我更喜歡,這是有道理的,但它仍然是一個可怕的論點。你正在編寫C++,它看起來像C++。假裝它是其他一些東西比一些美學必然性更可能產生不好的C++代碼。

+0

我理解你的論點,但我仍然認爲預處理器的設計很糟糕,它不是上下文感知的,它比較常見別名,比較: vector3 rotation;隨着歐拉旋轉,是的,你可以定義歐拉,但它不會再與其他vector3一起操作,或者用它來操作其他vector3。 無論如何,我使用#define的唯一原因是因爲替代方法不起作用,嗯,我想這並不重要 - 我只是稍微地使用我的代碼並使用typename,您的解釋意味着沒有其他合理的解決方案。 – LordHexahedron

+0

預處理器被調用_before_解析發生在所有,所以它不能合理地解析代碼來派生上下文。語法較簡單的另一種語言_應該具有更好的宏觀系統,但它不再是_pre_處理器。無論如何,這些東西幾十年來一直沒有改變,所以它不應該是一個驚喜。無論如何,你的評論中的例子似乎應該是'using'或'typedef'。 – Useless