/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단계 규칙 준수를 위해 코드 업데이트
이전 버전의 컴파일러에는 키워드가 template
typename
필요하지 않으며 C++ 표준에 필요한 모든 위치에서 키워드가 필요합니다. 이러한 키워드는 컴파일러가 조회의 첫 번째 단계에서 종속 이름을 구문 분석하는 방법을 명확히 하기 위해 일부 위치에서 필요합니다. 예시:
T::Foo<a || b>(c);
준수 컴파일러는 범위에서 T
변수로 구문 분석 Foo
합니다. 즉, 이 코드는 왼쪽 피연산자와 b > (c)
오른쪽 피연산자로 있는 논리 또는 식 T::foo < a
입니다. 함수 템플릿으로 사용 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-
아래에 /permissive- /Zc:twoPhase-
컴파일됩니다. 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-
컴파일러는 줄 2에만 키워드가 template
필요합니다. 준수 모드에서 컴파일러는 이제 템플릿임을 나타내 T::X<T>
기 위해 줄 4의 키워드도 필요합니다template
. 이 키워드가 누락된 코드를 찾아 코드를 표준에 맞게 제공합니다.
규칙 문제에 대한 자세한 내용은 Visual Studio 및 비표준 동작의 C++ 규칙 향상을 참조하세요.
Visual Studio 개발 환경에서 이 컴파일러 옵션을 설정하려면
프로젝트의 속성 페이지 대화 상자를 엽니다. 자세한 내용은 Visual Studio에서 C++ 컴파일러 및 빌드 속성 설정을 참조하세요.
구성 속성>C/C++>명령줄 속성 페이지를 선택합니다.
포함
/Zc:twoPhase-
하도록 추가 옵션 속성을 수정한 다음 확인을 선택합니다.