/Zc:twoPhase- (2단계 이름 조회를 사용하지 않도록 설정)

아래/permissive-의 옵션은 /Zc:twoPhase- 컴파일러에 원래의 비준수 Microsoft C++ 컴파일러 동작을 사용하여 클래스 템플릿 및 함수 템플릿을 구문 분석하고 인스턴스화하도록 지시합니다.

구문

/Zc:twoPhase-

설명

Visual Studio 2017 버전 15.3 이상: 아래에서 /permissive-컴파일러는 템플릿 이름 확인을 위해 2단계 이름 조회를 사용합니다. 또한 지정/Zc:twoPhase-하는 경우 컴파일러는 이전의 비준수 클래스 템플릿 및 함수 템플릿 이름 확인 및 대체 동작으로 되돌리기. /permissive- 지정하지 않으면 비준수 동작이 기본값입니다.

버전 10.0.15063.0(크리에이터스 업데이트 또는 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

컴파일러가 템플릿에 도달할 때 두 번째 오버로드 func 가 표시되지 않으므로 이 프로그램은 준수 모드에서 /permissive-컴파일될 때 "Standard two-phase"를 출력합니다. 추가 /Zc:twoPhase-하면 프로그램이 "Microsoft one-phase"를 출력합니다. 출력은 지정 /permissive-하지 않은 경우와 동일합니다.

종속 이름은 템플릿 매개 변수에 따라 달라지는 이름입니다. 이러한 이름에는 아래에서도 다른 조회 동작이 있습니다 /Zc:twoPhase-. 준수 모드에서는 종속 이름이 템플릿 정의의 지점에서 바인딩되지 않습니다. 대신 컴파일러는 템플릿을 인스턴스화할 때 조회합니다. 종속 함수 이름을 가진 함수 호출의 경우 이름은 템플릿 정의의 호출 사이트에 표시되는 함수에 바인딩됩니다. 인수 종속 조회의 다른 오버로드는 템플릿 정의의 지점과 템플릿 인스턴스화 시점에 추가됩니다.

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단계 규칙 준수를 위해 코드 업데이트

이전 버전의 컴파일러는 키워드(keyword) templatetypename 필요하지 않으며 C++ 표준에 필요한 모든 위치에서 필요합니다. 이러한 키워드(keyword) 컴파일러가 조회의 첫 번째 단계에서 종속 이름을 구문 분석하는 방법을 명확하게 하기 위해 일부 위치에서 필요합니다. 예시:

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

준수 컴파일러는 범위에서 T변수로 구문 분석 Foo 합니다. 즉, 이 코드는 왼쪽 피연산자와 b > (c) 오른쪽 피연산자로 있는 논리 또는 식 T::foo < a 입니다. 함수 템플릿으로 사용 Foo 하려는 경우 키워드(keyword) 추가하여 template 템플릿임을 나타내야 합니다.

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

Visual Studio 2017 버전 15.3 이상 버전에서 컴파일러는 지정된 경우 /permissive-/Zc:twoPhase- 키워드(keyword) 없이 template 이 코드를 허용합니다. 제한된 방식으로 템플릿만 구문 분석하기 때문에 인수 a || b가 있는 함수 템플릿에 대한 호출로 코드를 해석합니다. 위의 코드는 첫 번째 단계에서 전혀 구문 분석되지 않습니다. 두 번째 단계에서는 변수가 아닌 템플릿임을 T::Foo 알 수 있는 충분한 컨텍스트가 있으므로 컴파일러가 키워드(keyword) 사용을 적용하지 않습니다.

함수 템플릿 본문, 이니셜라이저, 기본 인수 및 noexcept 인수의 이름 앞에 키워드(keyword) typename 제거하여 이 동작을 확인할 수도 있습니다. 예시:

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

함수 본문에서 키워드(keyword) typename 사용하지 않는 경우 이 코드는 단독으로 컴파일되지 않고 /permissive- 아래에 /permissive- /Zc:twoPhase-컴파일됩니다. typename 키워드(keyword) 종속되어 있음을 TYPE 나타내기 위해 필요합니다. 본문은 아래 /Zc:twoPhase-구문 분석되지 않으므로 컴파일러에 키워드(keyword) 필요하지 않습니다. 규칙 모드에서 /permissive- 키워드(keyword) 없는 코드는 typename 오류를 생성합니다. Visual Studio 2017 버전 15.3 이상에서 코드를 준수하도록 마이그레이션하려면 누락된 키워드(keyword) 삽입 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)만 필요로 합니다. 준수 모드에서 컴파일러는 이제 4줄의 template 키워드(keyword) 템플릿임을 나타내 T::X<T> 야 합니다. 이 키워드(keyword) 누락된 코드를 찾아 코드를 표준에 맞게 제공합니다.

규칙 문제에 대한 자세한 내용은 Visual Studio비표준 동작의 C++ 규칙 향상을 참조하세요.

Visual Studio 개발 환경에서 이 컴파일러 옵션을 설정하려면

  1. 프로젝트의 속성 페이지 대화 상자를 엽니다. 자세한 내용은 Visual Studio에서 C++ 컴파일러 및 빌드 속성 설정을 참조하세요.

  2. 구성 속성>C/C++>명령줄 속성 페이지를 선택합니다.

  3. 포함 /Zc:twoPhase- 하도록 추가 옵션 속성을 수정한 다음 확인을 선택합니다.

참고 항목

/Zc (규칙)