/Zc:ternary
(조건부 연산자 규칙 적용)
조건부 연산자 식에서 두 번째 및 세 번째 피연산자의 형식 및 const 또는 volatile(cv) 한정에 대해 C++ 표준 규칙을 적용할 수 있습니다.
구문
/Zc:ternary
[-
]
설명
Visual Studio 2017부터 컴파일러는 C++ 표준 조건부 연산 자(?:
) 동작을 지원합니다. 삼항 연산자라고도 합니다. C++ 표준에는 세 가지 조건 중 하나를 충족하는 3개의 피연산자가 필요합니다. 피연산자는 형식 및 자격(cv-qualification)이 const
volatile
동일해야 합니다. 또는 하나의 피연산자만 다른 피연산자와 동일한 형식 및 cv-qualification으로 명확하게 변환할 수 있어야 합니다. 또는 하나 또는 두 피연산자는 throw 식이어야 합니다. Visual Studio 2017 버전 15.5 이전 버전에서 컴파일러는 표준에 의해 모호한 것으로 간주되는 변환을 허용했습니다.
/Zc:ternary
옵션을 지정하면 컴파일러가 표준을 준수합니다. 일치하는 형식에 대한 규칙과 두 번째 및 세 번째 피연산자의 cv-qualification을 충족하지 않는 코드를 거부합니다.
Visual /Zc:ternary
Studio 2017에서는 이 옵션이 기본적으로 해제되어 있습니다. 준수 동작을 사용하도록 설정하거나 /Zc:ternary-
이전의 비규격 컴파일러 동작을 명시적으로 지정하는 데 사용합니다/Zc:ternary
. 이 /permissive-
옵션은 암시적으로 이 옵션을 사용하도록 설정하지만 /Zc:ternary-
.
예제
이 샘플에서는 형식에서 명시적이지 않은 초기화와 형식으로의 변환을 모두 제공하는 클래스가 모호한 변환으로 이어질 수 있는 방법을 보여 줍니다. 이 코드는 기본적으로 컴파일러에서 허용되지만 지정되거나 /permissive-
지정된 경우 /Zc:ternary
거부됩니다.
// zcternary1.cpp
// Compile by using: cl /EHsc /W4 /nologo /Zc:ternary zcternary1.cpp
struct A
{
long l;
A(int i) : l{i} {} // explicit prevents conversion of int
operator int() const { return static_cast<int>(l); }
};
int main()
{
A a(42);
// Accepted when /Zc:ternary (or /permissive-) is not used
auto x = true ? 7 : a; // old behavior prefers A(7) over (int)a
auto y = true ? A(7) : a; // always accepted
auto z = true ? 7 : (int)a; // always accepted
return x + y + z;
}
이 코드를 수정하려면 기본 설정 공통 형식으로 명시적 캐스트를 만들거나 형식 변환의 한 방향을 방지합니다. 변환을 명시적으로 만들어 컴파일러가 형식 변환과 일치하지 않도록 할 수 있습니다.
이 일반적인 패턴의 중요한 예외는 피연산자의 형식이 null로 끝나는 문자열 형식(예: const char*
등 const char16_t*
)인 경우입니다. 배열 형식과 해당 형식이 감쇠하는 포인터 형식을 사용하여 효과를 재현할 수도 있습니다. 실제 두 번째 또는 세 번째 피연산자를 ?:
해당 형식의 문자열 리터럴인 경우의 동작은 사용되는 언어 표준에 따라 달라집니다. C++17이 이 사례의 의미 체계를 C++14에서 변경했습니다. 따라서 컴파일러는 다음 예제의 코드를 기본값 /std:c++14
으로 수락하지만 지정하거나 나중에 지정 /std:c++17
하면 거부합니다.
// zcternary2.cpp
// Compile by using: cl /EHsc /W4 /nologo /Zc:ternary /std:c++17 zcternary2.cpp
struct MyString
{
const char * p;
MyString(const char* s = "") noexcept : p{s} {} // from char*
operator const char*() const noexcept { return p; } // to char*
};
int main()
{
MyString s;
auto x = true ? "A" : s; // MyString: permissive prefers MyString("A") over (const char*)s
}
이 코드를 수정하려면 피연산자 중 하나를 명시적으로 캐스팅합니다.
아래에서 /Zc:ternary
컴파일러는 인수 중 하나가 형식 void
이고 다른 하나는 식이 아닌 조건부 연산자를 throw
거부합니다. 이 패턴의 일반적인 용도는 ASSERT와 유사한 매크로입니다.
// zcternary3.cpp
// Compile by using: cl /EHsc /W4 /nologo /Zc:ternary /c zcternary3.cpp
void myassert(const char* text, const char* file, int line);
#define ASSERT(ex) (void)((ex) ? 0 : myassert(#ex, __FILE__, __LINE__))
// To fix, define it this way instead:
// #define ASSERT(ex) (void)((ex) ? void() : myassert(#ex, __FILE__, __LINE__))
int main()
{
ASSERT(false); // C3447
}
일반적인 해결 방법은 void가 아닌 인수를 .로 void()
바꾸는 것입니다.
이 샘플에서는 다음과 같은 두 /Zc:ternary
/Zc:ternary-
가지 모두에서 오류를 생성하는 코드를 보여 줍니다.
// zcternary4.cpp
// Compile by using:
// cl /EHsc /W4 /nologo /Zc:ternary zcternary4.cpp
// cl /EHsc /W4 /nologo /Zc:ternary zcternary4.cpp
int main() {
auto p1 = [](int a, int b) { return a > b; };
auto p2 = [](int a, int b) { return a > b; };
auto p3 = true ? p1 : p2; // C2593 under /Zc:ternary, was C2446
}
이 코드는 이전에 이 오류를 제공했습니다.
error C2446: ':': no conversion from 'foo::<lambda_f6cd18702c42f6cd636bfee362b37033>' to 'foo::<lambda_717fca3fc65510deea10bc47e2b06be4>'
note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
이 /Zc:ternary
경우 실패의 이유가 더 명확해집니다. 여러 구현 정의 호출 규칙을 사용하여 각 람다를 생성할 수 있습니다. 그러나 컴파일러에는 가능한 람다 서명을 명확하게 하는 기본 설정 규칙이 없습니다. 새 출력은 다음과 같습니다.
error C2593: 'operator ?' is ambiguous
note: could be 'built-in C++ operator?(bool (__cdecl *)(int,int), bool (__cdecl *)(int,int))'
note: or 'built-in C++ operator?(bool (__stdcall *)(int,int), bool (__stdcall *)(int,int))'
note: or 'built-in C++ operator?(bool (__fastcall *)(int,int), bool (__fastcall *)(int,int))'
note: or 'built-in C++ operator?(bool (__vectorcall *)(int,int), bool (__vectorcall *)(int,int))'
note: while trying to match the argument list '(foo::<lambda_717fca3fc65510deea10bc47e2b06be4>, foo::<lambda_f6cd18702c42f6cd636bfee362b37033>)'
일반적인 문제의 /Zc:ternary
원인은 템플릿 메타 프로그래밍에 사용되는 조건부 연산자에서 비롯됩니다. 이 스위치에서 일부 결과 형식이 변경되었습니다. 다음 예제에서는 메타 프로그래밍이 아닌 컨텍스트에서 조건식의 결과 형식을 변경하는 두 가지 경우 /Zc:ternary
를 보여 줍니다.
// zcternary5.cpp
// Compile by using: cl /EHsc /W4 /nologo /Zc:ternary zcternary5.cpp
int main(int argc, char**) {
char a = 'A';
const char b = 'B';
decltype(auto) x = true ? a : b; // char without, const char& with /Zc:ternary
const char(&z)[2] = argc > 3 ? "A" : "B"; // const char* without /Zc:ternary
return x > *z;
}
일반적인 수정 사항은 이전 동작을 std::remove_reference
유지하는 데 필요한 결과 형식에 특성을 적용하는 것입니다.
Visual C++의 규칙과 관련된 문제에 대한 자세한 내용은 Nonstandard Behavior을 참조하세요.
Visual Studio 개발 환경에서 이 컴파일러 옵션을 설정하려면
프로젝트의 속성 페이지 대화 상자를 엽니다. 자세한 내용은 Visual Studio에서 C++ 컴파일러 및 빌드 속성 설정을 참조하세요.
구성 속성>C/C++>명령줄 속성 페이지를 선택합니다.
추가 옵션 속성을 수정하여 포함
/Zc:ternary
하거나/Zc:ternary-
선택한 다음 확인을 선택합니다.