Share via


/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) 的多載集,即使它在函式範本之後宣告。

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

舊版的編譯器不需要 關鍵字,而且 C++ Standard 無處不在都需要關鍵字 templatetypename 。 某些位置需要這些關鍵字,以厘清編譯器在查閱第一個階段中剖析相依名稱的方式。 例如:

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 Studio 中的 C++ 一致性改善和非 標準行為

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

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

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

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

另請參閱

/Zc (一致性)