/Zc:twoPhase- (2 フェーズの名前参照を無効にする)

このオプションの下/permissive-にあるこのオプションは/Zc:twoPhase-、元の準拠していない Microsoft C++ コンパイラの動作を使用して、クラス テンプレートと関数テンプレートを解析してインスタンス化するようにコンパイラに指示します。

構文

/Zc:twoPhase-

解説

Visual Studio 2017 バージョン 15.3 以降: コンパイラでは /permissive-、テンプレートの名前解決に 2 フェーズの名前参照が使用されます。 また、指定 /Zc:twoPhase-した場合、コンパイラは以前の準拠していないクラス テンプレートと関数テンプレートの名前解決と置換動作に戻ります。 指定しない場合 /permissive- 、準拠していない動作が既定です。

バージョン 10.0.15063.0 (Creators Update または RS2) 以前の Windows SDK ヘッダー ファイルは、準拠モードでは動作しません。 /Zc:twoPhase- を使用 /permissive-する場合は、これらの SDK バージョンのコードをコンパイルする必要があります。 バージョン 10.0.15254.0 (Fall Creators Update または RS3) 以降のバージョンの Windows SDK は、準拠モードで正しく動作します。 オプションは必要 /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-でコンパイルすると、コンパイラがテンプレートに到達したときに 2 番目のfuncオーバーロードが表示されないため、このプログラムは "Standard two-phase" を出力します。 追加 /Zc:twoPhase-すると、プログラムは "" をMicrosoft one-phase出力します。 出力は、指定 /permissive-しない場合と同じです。

依存名は、テンプレート パラメーターに依存する名前です。 これらの名前の参照動作は、次の下 /Zc:twoPhase-でも異なります。 準拠モードでは、依存名がテンプレートの定義の時点でバインドされていません。 代わりに、テンプレートをインスタンス化するときに、コンパイラによって検索されます。 依存する関数名を持つ関数呼び出しの場合、名前は、テンプレート定義の呼び出しサイトで表示される関数にバインドされます。 引数依存参照の他のオーバーロードは、テンプレート定義のポイントとテンプレートインスタンス化の時点の両方に追加されます。

2 フェーズ参照は、テンプレートが定義される時点での非依存名の参照と、テンプレートがインスタンス化される時点での依存名の参照という 2 つの部分で構成されます。 コンパイラ /Zc:twoPhase-では、非修飾参照とは別に引数依存の参照は実行されません。 つまり、2 フェーズ参照が行われないため、オーバーロード解決の結果が異なる場合があります。

別の例を示します。

// 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) 解決されます。 次のように/Zc:twoPhase-、オーバーロードにはvoid func(int)解決されません。 その理由は、非修飾 func(int) はテンプレートの定義の後に宣言され、引数依存の参照では見つからないためです。 ただし、 void func(S) 引数依存の参照に参加するため、関数テンプレートの後に宣言されている場合でも、呼び出し tfunc(s)のオーバーロード セットに追加されます。

2 フェーズで準拠するようにコードを更新する

以前のバージョンのコンパイラでは、C++ 標準で必要とされるすべての場所で、キーワード templatetypename は必要とされません。 これらのキーワードは、参照の最初のフェーズでコンパイラが依存名を解析する方法を明確にするために、いくつかの位置で必要になります。 次に例を示します。

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

準拠するコンパイラは、T のスコープ内で変数として Foo を解析します。つまり、このコードは、左側のオペランドとして T::foo < a、右側のオペランドとして b > (c) が指定された論理 OR 式です。 関数テンプレートとして Foo を使用する場合は、template キーワードを追加することで、それがテンプレートであることを示す必要があります。

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

Visual Studio 2017 バージョン 15.3 以降では、指定されている場合/permissive-/Zc:twoPhase-コンパイラはキーワード (keyword)なしでこのコードをtemplate許可します。 このコードは、限定された方法でテンプレートを解析するだけなので、a || b の引数を持つ関数テンプレートへの呼び出しとして解釈されます。 上記のコードは、最初のフェーズでは何も解析されません。 2 番目のフェーズでは、T::Foo が変数ではなくテンプレートであることを示すのに十分なコンテキストがあるため、コンパイラはキーワードの使用を強制しません。

この動作は、関数テンプレート本体、初期化子、既定の引数、および noexcept 引数の名前の前でキーワード typename を削除することによっても確認できます。 次に例を示します。

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

関数本体でキーワード (keyword)typenameを使用しない場合、このコードはコンパイルは/permissive- /Zc:twoPhase-行われますが、単独ではコンパイルされません/permissive-typename キーワードは、TYPE が依存していることを示すために必要です。 本体は解析されていないので/Zc:twoPhase-、コンパイラはキーワード (keyword)を必要としません。 準拠モードでは/permissive-、キーワード (keyword)のない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-以前のコンパイラでは、コンパイラは 2 行目のtemplateキーワード (keyword)のみを必要とします。 準拠モードでは、T::X<T> がテンプレートであることを示すために、4 行目の template キーワードも必要になりました。 このキーワードが欠落しているコードを探し、コードが標準に準拠するようにキーワードを指定します。

準拠の問題の詳細については、Visual Studio非標準動作での C++ 準拠の機能強化に関するページを参照してください。

Visual Studio 開発環境でこのコンパイラ オプションを設定するには

  1. プロジェクトの [プロパティ ページ] ダイアログ ボックスを開きます。 詳細については、Visual Studio での C++ コンパイラとビルド プロパティの設定に関する記事を参照してください。

  2. [構成プロパティ]>[C/C++]>[コマンド ライン] プロパティ ページを選択します。

  3. /Zc:twoPhase- が含まれるように [追加のオプション] プロパティを変更し、[OK] を選択します。

関連項目

/Zc (準拠)