1. 錯誤輸出
./zlibrary/ui/src/win32/w32widgets/W32VBorderBox.cpp(114) : error C2589: “(”: “::”右邊的非法標記
./zlibrary/ui/src/win32/w32widgets/W32VBorderBox.cpp(114) : error C2059: 語法錯誤 : “::”
2. 錯誤代碼舉例
size.Width = std::max(size.Width, elementSize.Width);
3. 函數模板max
template<class _Ty> inline const _Ty& (__CLRCALL_OR_CDECL max)(const _Ty& _Left, const _Ty& _Right) { // return larger of _Left and _Right return (_DEBUG_LT(_Left, _Right) ? _Right : _Left); }
注:模板就是實現代碼重用機制的一種工具,它可以實現類型參數化,即把類型定義為參數, 從而實現了真正的代碼可重用性。模版可以分為兩類,一個是函數模版,另外一個是類模版。
4. 錯誤原因
函數模板max與Visual C++中的全局的宏max沖突。
5. 解決辦法
第一種辦法:設置項目屬性,在預定義處理器中添加定義NOMINMAX來禁止使用Vsual C++的min/max宏定義。
項目屬性 ——> C/C++ ——> 預處理器 ——> 預處理器定義 (此處添加預定義編譯開關 NOMINMAX)
第二種辦法: 加上括號,與Vsual C++的min/max宏定義區分開
size.Width = std::max(size.Width, elementSize.Width);
修改為:
size.Width = (std::max)(size.Width, elementSize.Width);
特別聲明:以上文章內容摘抄自:原文章鏈接
在后面的程序中調試,在加入到MFC工程后,出現下列問題:
1>c:\program files\microsoft sdks\windows\v6.0a\include\GdiplusTypes.h(470) : error C3861: 'min': identifier not found
1>c:\program files\microsoft sdks\windows\v6.0a\include\GdiplusTypes.h(471) : error C3861: 'min': identifier not found
1>c:\program files\microsoft sdks\windows\v6.0a\include\GdiplusTypes.h(472) : error C3861: 'max': identifier not found
1>c:\program files\microsoft sdks\windows\v6.0a\include\GdiplusTypes.h(473) : error C3861: 'max': identifier not found
其實只是通過修改編譯器中c++屬性解決不了真正的問題,關鍵還是頭文件的引用順序。當然出現上述問題,再把預處理定義器中NOMINMAX去掉,同時進行關鍵問題修改c++頭文件引用順序的修改。
下面這篇便是對頭文件引用順序的最好解釋。
1. 為了加強可讀性和避免隱含依賴,應使用下面的順序:C標准庫、C++標准庫、其它庫的頭文件、你自己工程的頭文件(.h文件包含順序。不過這里最先包含的是首選的頭文件,即例如a.cpp文件中應該優先包含a.h(.cpp文件中先包含自己的.h文件,再包含標准庫等等頭文件。首選的頭文件是為了減少隱藏依賴,同時確保頭文件和實現文件是匹配的。具體的例子是:假如你有一個cc文件(linux平台的cpp文件后綴為cc)是google-awesome-project/src/foo/internal/fooserver.cc,那么它所包含的頭文件的順序如下:
<span style="font-size:16px;">#include "foo/public/fooserver.h" // Preferred location.
#include <sys/types.h>
#include <unistd.h>
#include <hash_map>
#include <vector>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/public/bar.h"
</span>
2. 在包含頭文件時應該加上頭文件所在工程的文件夾名,即假如你有這樣一個工程base,里面有一個logging.h,那么外部包含這個頭文件應該這樣寫:
#include "base/logging.h",而不是#include "logging.h"
我們看到的是這里《Google C++ 編程風格指南》倡導的原則背后隱藏的目的是:
1. 為了減少隱藏依賴,同時頭文件和其實現文件匹配,應該先包含其首選項(即其對應的頭文件)。
2. 除了首選項外,遵循的是從一般到特殊的原則。不過我覺得《Google C++ 編程風格指南》的順序:C標准庫、C++標准庫、其它庫的頭文件、你自己工程的頭文件中漏了最前面的一項:操作系統級別的頭文件,比如上面的例子sys/types.h估計不能歸入C標准庫,而是Linux操作系統提供的SDK吧。因此我覺得更准確的說法應該是:OS SDK .h , C標准庫、C++標准庫、其它庫的頭文件、你自己工程的頭文件。
3.之所以要將頭文件所在的工程目錄列出,作用應該是命名空間是一樣的,就是為了區分不小心造成的文件重名。
二.《C++編程思想》中的不同觀點
與《Google C++ 編程風格指南》不同的是,《C++編程思想》倡導一種不同的規則。《C++編程思想》P432提到:
頭文件被包含的順序是從“最特殊到最一般”。這就是,在本地目錄的任何頭文件首先被包含。然后是我們自己的所有“工具”頭文件,隨后是第三方庫頭文件,接着是標准C++庫頭文件和C庫頭文件。
要了解其原因:可以看JohnLakos在《Large ScaleC++ Softwre Design》(注:其中文譯名為《大規模C++程序設計》)中的一段話:
保證.h文件的組成部分不被它自身解析(parse),這可以避免潛在的使用錯誤。因為被自身解析缺乏明確提供的聲明或定義。在.c文件的第一行包含.h 文件能確保所有對於構件的物理界面重要的內部信息塊都在.h中(如果的確是缺少了某些信息塊,一旦編譯這個.c文件時就可以發現這個問題)。
如果包含頭文件的順序是“從最特殊到最一般”,如果我們的頭文件不被它自己解析。我們將馬上找到它,防止麻煩事情發生。
三.我的試驗
到底哪一種包含順序好呢?我使用VS 2005編一個控制台測試工程TestInc,里面有幾個文件。
MyMath.h的代碼如下:
<span style="font-size:16px;">#pragma once
#pragma once
double acos(double Num);
</span>
MyMath.cpp的代碼如下:
<span style="font-size:16px;">double acos(double Num)
{
return 1.0;
}
</span>
TestInc.cpp的代碼如下:
<span style="font-size:16px;">#include "stdafx.h"
#include "TestInc.h"
#include <stdio.h>
#include <math.h>
int _tmain(int argc, _TCHAR* argv[])
{
double a = acos(0.5);
return 0;
}
</span>
結果出現錯誤:
1>c:\program files\microsoft visualstudio 8\vc\include\math.h(107) : error C2732: 鏈接規范與“acos”的早期規范沖突
1> c:\program files\microsoft visual studio 8\vc\include\math.h(107) : 參見“acos”的聲明
然后我把TestInc.cpp的頭文件包含順序改為:
<span style="font-size:16px;">#include "stdafx.h"
#include <stdio.h>
#include <math.h>
#include "TestInc.h"
</span>
則編譯通過了。在調試運行時main函數調用還是C標准庫的函數acos,看來函數調用的順序是按頭文件的包含順序來的,即我自定義的acos函數被覆蓋了(如果TestInc.h里包含了內聯函數,則優先調用的是內聯函數)。
從這個小實驗中我得出如下結論:《Google C++ 編程風格指南》和《C++編程思想》倡導的包含頭文件的順序各有優點,《Google C++ 編程風格指南》應該能大量減少隱藏的頭文件依賴,而《C++編程思想》則很容易讓你清楚知道你所定義的接口是否和系統庫及第三方庫發生沖突。
四.頭文件包含中的預編譯功能
在Visual Studio環境下開發我們發現幾乎每個cpp文件都要包含stdafx.h這個文件,而且要把它放在最前面的位置,否則就會出錯。這是為什么呢?
原來Visual Studio采用一種預編譯的機制。要了解預編譯機制,先介紹一下預編譯頭。所謂的預編譯頭就是把一個工程中的那一部分代碼,預先編譯好放在一個文件里(通常是以.pch為擴展名的),這個文件就稱為預編譯頭文件這些預先編譯好的代碼可以是任何的C/C++代碼,甚至是inline的函數,但是必須是穩定的,在工程開發的過程中不會被經常改變。如果這些代碼被修改,則需要重新編譯生成預編譯頭文件。注意生成預編譯頭文件是很耗時間的。同時你得注意預編譯頭文件通常很大,通常有6- 7M大。注意及時清理那些沒有用的預編譯頭文件。
也許你會問:現在的編譯器都有Time stamp的功能,編譯器在編譯整個工程的時候,它只會編譯那些經過修改的文件,而不會去編譯那些從上次編譯過,到現在沒有被修改過的文件。那么為什么還要預編譯頭文件呢?答案在這里,我們知道編譯器是以文件為單位編譯的,一個文件經過修改后,會重新編譯整個文件,當然在這個文件里包含的所有頭文件中的東西(.eg Macro, Preprocessor )都要重新處理一遍。 VC的預編譯頭文件保存的正是這部分信息。以避免每次都要重新處理這些頭文件。
根據上文介紹,預編譯頭文件的作用當然就是提高便宜速度了,有了它你沒有必要每次都編譯那些不需要經常改變的代碼。編譯性能當然就提高了。
要使用預編譯頭,我們必須指定一個頭文件,這個頭文件包含我們不會經常改變的代碼和其他的頭文件,然后我們用這個頭文件來生成一個預編譯頭文件(.pch 文件)想必大家都知道StdAfx.h這個文件。很多人都認為這是VC提供的一個“系統級別”的,編譯器帶的一個頭文件。其實不是的,這個文件可以是任何名字的。我們來考察一個典型的由AppWizard生成的MFC Dialog Based 程序的預編譯頭文件。(因為AppWizard會為我們指定好如何使用預編譯頭文件,默認的是StdAfx.h,這是VC起的名字)。我們會發現這個頭文件里包含了以下的頭文件:
<span style="font-size:16px;">#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#include <afxdisp.h> // MFC Automation classes
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
#include <afxcmn.h>
</span>
這些正是使用MFC的必須包含的頭文件,當然我們不太可能在我們的工程中修改這些頭文件的,所以說他們是穩定的。
那么我們如何指定它來生成預編譯頭文件。我們知道一個頭文件是不能編譯的。所以我們還需要一個cpp文件來生成.pch 文件。這個文件默認的就是StdAfx.cpp。在這個文件里只有一句代碼就是:#include“Stdafx.h”。原因是理所當然的,我們僅僅是要它能夠編譯而已―――也就是說,要的只是它的.cpp的擴展名。我們可以用/Yc編譯開關來指定StdAfx.cpp來生成一個.pch文件,通過/Fp 編譯開關來指定生成的pch文件的名字。打開project ->Setting->C/C++ 對話框。把Category指向Precompiled Header。在左邊的樹形視圖里選擇整個工程,Project Options(右下角的那個白的地方)可以看到 /Fp “debug/PCH.pch”,這就是指定生成的.pch文件的名字,默認的通常是 <工程名>.pch。然后,在左邊的樹形視圖里選擇 StdAfx.cpp,這時原來的Project Option變成了 Source File Option(原來是工程,現在是一個文件,當然變了)。在這里我們可以看到 /Yc開關,/Yc的作用就是指定這個文件來創建一個Pch文件。/Yc后面的文件名是那個包含了穩定代碼的頭文件,一個工程里只能有一個文件的可以有 YC開關。VC就根據這個選項把 StdAfx.cpp編譯成一個Obj文件和一個PCH文件。
這樣,我們就設置好了預編譯頭文件。也就是說,我們可以使用預編譯頭功能了。以下是注意事項:
1)如果使用了/Yu,就是說使用了預編譯,我們在每個.cpp文件的最開頭,包含你指定產生pch文件的.h文件(默認是stdafx.h)不然就會有問題。如果你沒有包含這個文件,就告訴你Unexpected file end.
2)如果你把pch文件不小心丟了,根據以上的分析,你只要讓編譯器生成一個pch文件就可以了。也就是說把stdafx.cpp(即指定/Yc的那個cpp文件)重新編譯一遍就可以了。
那么在Linux平台下有沒有這種預編譯機制呢?如果有,它是怎么實現的呢?Linux平台下GCC編譯器也實現了預編譯機制的。這里以開源IDE CodeBlocks(CodeBlocks內置了GCC編譯器)的工程為例來說明Linux平台的實現:
使用CodeBlocks建一個C++工程,然后新建一個my_pch.h,輸入如下代碼:
<span style="font-size:16px;">/***************************************************************
* Name: my_pch.h
* Purpose: Header to create Pre-Compiled Header (PCH)
* Author: ()
* Created: 2010-10-26
* Copyright: ()
* License:
* 使用方法: 項目構建選項-->其他選項-->填入下面兩行
-Winvalid-pch
-include my_pch.h
**************************************************************/
#ifndef MY_PCH_H_INCLUDED
#define MY_PCH_H_INCLUDED
// put here all your rarely-changing header files
#include <iostream>
#include <string>
#endif
</span>
然后在項目構建選項-->其他選項-->填入下面兩行 -Winvalid-pch -include my_pch.h
就可以啟用預編譯文件頭。 然后 main.cpp 就可以不用 include 頭文件了,直接這樣就可以編譯了,
<span style="font-size:16px;">int main()
{
using namespace std;
cout << "Hello world!" << endl;
return 0;
}
</span>
即使在上面的代碼寫上下面一行,其實是不起作用的:
<span style="font-size:16px;">#include <iostream> </span>