共用方式為


/Zc:twoPhase- (停用兩階段名稱查閱)

選項/Zc:twoPhase-/permissive-會指示編譯程式使用原始、不符合規範Microsoft C++編譯程式行為來剖析和具現化類別範本和函式範本。

語法

/Zc:twoPhase-

備註

Visual Studio 2017 15.3 版和更新版本:在 下 /permissive-,編譯程式會針對範本名稱解析使用兩階段名稱查閱。 如果您也指定 /Zc:twoPhase-,編譯程式會還原成其先前不符合規範的類別範本和函式範本名稱解析和替代行為。 未指定 時 /permissive- ,不符合規範的行為是預設值。

10.0.15063.0 版(Creators Update 或 RS2)和更早版本中的 Windows SDK 頭文件無法以一致性模式運作。 /Zc:twoPhase- 當您使用 /permissive-時,需要編譯這些 SDK 版本的程式代碼。 從 10.0.15254.0 版開始的 Windows SDK 版本(Fall Creators Update 或 RS3)在一致性模式中正常運作。 它們不需要 /Zc:twoPhase- 選項。

如果您的程式代碼需要舊的行為才能正確編譯,請使用 /Zc:twoPhase- 。 強烈建議您更新程序代碼以符合標準。

下的編譯程序行為 /Zc:twoPhase-

根據預設,或在 Visual Studio 2017 15.3 版和更新版本中,當您同時指定 /permissive-/Zc:twoPhase-時,編譯程式會使用此行為:

  • 它只會剖析樣板宣告、類別前端和基類清單。 範本主體會擷取為令牌數據流。 不會剖析函式主體、初始化表達式、預設自變數或 noexcept 自變數。 類別範本會在暫定型別上進行虛擬具現化,以驗證類別範本中的宣告是否正確。 請考慮此類別範本:

    template <typename T> class Derived : public Base<T> { ... }
    

    範本宣告、 template <typename T>類別前端 class Derived和基類清單 public Base<T> 會剖析,但範本主體會擷取為令牌數據流。

  • 剖析函式範本時,編譯程式只會剖析函式簽章。 函式主體永遠不會剖析。 相反地,它會擷取為令牌數據流。

因此,如果範本主體有語法錯誤,但範本永遠不會具現化,則編譯程式不會診斷錯誤。

此行為的另一個效果是在多載解析中。 非標準行為是因為令牌數據流在具現化月臺擴充的方式而發生。 在範本宣告上看不到的符號可能會在具現化時顯示。 這表示他們可以參與多載解析。 您可能會發現範本會根據範本定義中看不到的程式代碼來變更行為,這與標準相反。

例如,請參考這個程式碼:

// zctwophase.cpp
// To test options, compile by using
// cl /EHsc /nologo /W4 zctwophase.cpp
// cl /EHsc /nologo /W4 /permissive- zctwophase.cpp
// cl /EHsc /nologo /W4 /permissive- /Zc:twoPhase- zctwophase.cpp

#include <cstdio>

void func(long) { std::puts("Standard two-phase") ;}

template<typename T> void g(T x)
{
    func(0);
}

void func(int) { std::puts("Microsoft one-phase"); }

int main()
{
    g(6174);
}

以下是當您搭配 /Zc:twoPhase- 編譯程式選項使用預設模式、一致性模式和一致性模式時的輸出:

C:\Temp>cl /EHsc /nologo /W4 zctwophase.cpp && zctwophase
zctwophase.cpp
Microsoft one-phase

C:\Temp>cl /EHsc /nologo /W4 /permissive- zctwophase.cpp && zctwophase
zctwophase.cpp
Standard two-phase

C:\Temp>cl /EHsc /nologo /W4 /permissive- /Zc:twoPhase- zctwophase.cpp && zctwophase
zctwophase.cpp
Microsoft one-phase

在下 /permissive-以一致性模式編譯時,此程式會列印 “Standard two-phase”,因為編譯程式到達範本時看不到 的第二個 多載 func 。 如果您新增 /Zc:twoPhase-,程式會列印 “Microsoft one-phase ” 輸出與您未指定 /permissive-時相同。

相依名稱 是相依於範本參數的名稱。 這些名稱在下 /Zc:twoPhase-也有不同的查閱行為。 在一致性模式中,相依名稱不會系結於範本定義點。 相反地,編譯程式會在具現化範本時查閱它們。 對於具有相依函式名稱的函式呼叫,此名稱會系結至範本定義中呼叫月臺可見的函式。 從自變數相依查閱新增其他多載,無論是在範本定義的點,還是範本具現化點。

兩階段查閱包含兩個部分:在範本定義期間查閱非相依名稱,以及在範本具現化期間查閱相依名稱。 在 下 /Zc:twoPhase-,編譯程式不會與不合格的查閱分開執行自變數相依查閱。 也就是說,它不會進行雙階段查閱,因此多載解析的結果可能會不同。

以下是另一個範例:

// zctwophase1.cpp
// To test options, compile by using
// cl /EHsc /W4 zctwophase1.cpp
// cl /EHsc /W4 /permissive- zctwophase1.cpp
// cl /EHsc /W4 /permissive- /Zc:twoPhase- zctwophase1.cpp

#include <cstdio>

void func(long) { std::puts("func(long)"); }

template <typename T> void tfunc(T t) {
    func(t);
}

void func(int) { std::puts("func(int)"); }

namespace NS {
    struct S {};
    void func(S) { std::puts("NS::func(NS::S)"); }
}

int main() {
    tfunc(1729);
    NS::S s;
    tfunc(s);
}

在沒有 的情況下編譯 /permissive-時,此程式代碼會列印:

func(int)
NS::func(NS::S)

使用 /permissive-編譯但不含 /Zc:twoPhase-時,此程式代碼會列印:

func(long)
NS::func(NS::S)

使用 /permissive-/Zc:twoPhase-編譯時,此程式代碼會列印:

func(int)
NS::func(NS::S)

在的一致性模式中 /permissive-,呼叫 tfunc(1729) 會解析為 void func(long) 多載。 它不會解析為 void func(int) 多載,如 下 /Zc:twoPhase-所示。 原因是,在範本定義之後宣告不合格的 func(int) ,而且無法透過自變數相依查閱找到。 但是 void func(S) 會參與自變數相依查閱,因此它會新增至呼叫 tfunc(s)的多載集,即使它在函式範本之後宣告。

更新程式代碼以取得兩階段一致性

舊版的編譯程式不需要 關鍵詞templatetypename,C++ Standard 需要它們。 某些位置需要這些關鍵詞,以釐清編譯程式在查閱第一個階段中剖析相依名稱的方式。 例如:

T::Foo<a || b>(c);

符合規範的編譯程式會 Foo 剖析為 範圍 T中的變數,這表示此程式代碼是邏輯或表達式,具有 T::foo < a 做為左操作數和 b > (c) 右操作數。 如果您表示要當做函式範本使用 Foo ,您必須藉由新增 template 關鍵詞來指出它是範本:

T::template Foo<a || b>(c);

在 Visual Studio 2017 15.3 版和更新版本中,指定和 時/permissive-/Zc:twoPhase-,編譯程式允許此程式代碼沒有 template 關鍵詞。 它會將程式代碼解譯為對具有 自變數 a || b之函式範本的呼叫,因為它只會以有限的方式剖析範本。 上述程式代碼在第一個階段完全不會剖析。 在第二個階段中,有足夠的內容可告知 T::Foo 這是範本,而不是變數,因此編譯程式不會強制使用 關鍵詞。

您也可以藉由在函式範本主體、初始化運算式、預設自變數和 noexcept 自變數中排除關鍵詞 typename ,來查看此行為。 例如:

template<typename T>
typename T::TYPE func(typename T::TYPE*)
{
    /* typename */ T::TYPE i;
}

如果您未在函式主體中使用 關鍵詞 typename ,則此程式代碼會在下 /permissive- /Zc:twoPhase-編譯,但不會單獨編譯 /permissive- 。 必須 typename 有 關鍵詞,才能指出 TYPE 相依。 因為主體並未剖析在 下 /Zc:twoPhase-,因此編譯程式不需要 關鍵詞。 在 /permissive- 一致性模式中,沒有 關鍵詞的程式 typename 代碼會產生錯誤。 若要將程式代碼移轉至 Visual Studio 2017 15.3 版和更新版本中的一致性,請插入 typename 遺漏的 關鍵詞。

同樣地,請考慮下列程式代碼範例:

template<typename T>
typename T::template X<T>::TYPE func(typename T::TYPE)
{
    typename T::/* template */ X<T>::TYPE i;
}

在舊版編譯程式下 /permissive- /Zc:twoPhase- 和中,編譯程式只需要 template 第 2 行的 關鍵詞。 在一致性模式中,編譯程序現在也需要 template 第 4 行的 關鍵詞,以指出 T::X<T> 這是範本。 尋找遺漏此關鍵詞的程式代碼,並提供它讓您的程式代碼符合標準。

如需一致性問題的詳細資訊,請參閱 Visual StudioNonstandard 行為的C++一致性改善。

在 Visual Studio 開發環境中設定這個編譯器選項

  1. 開啟專案的 [屬性頁] 對話方塊。 如需詳細資料,請參閱在 Visual Studio 中設定 C ++ 編譯器和組建屬性

  2. 選取 [組態屬性]>[C/C++]>[命令列] 屬性頁。

  3. 修改 [其他選項] 屬性以包含 /Zc:twoPhase- ,然後選擇 [確定]。

另請參閱

/Zc (一致性)