/Zc:ternary (조건부 연산자 규칙 적용)

조건부 연산자 식에서 두 번째 및 세 번째 피연산자의 형식 및 const 또는 volatile(cv) 한정에 대해 C++ 표준 규칙을 적용할 수 있습니다.

구문

/Zc:ternary[-]

설명

Visual Studio 2017부터 컴파일러는 C++ 표준 조건부 연산 자(?:) 동작을 지원합니다. 삼항 연산라고도 합니다. C++ 표준에는 세 가지 조건 중 하나를 충족하는 3개의 피연산자가 필요합니다. 피연산자는 형식 및 자격(cv-qualification)이 constvolatile 동일해야 합니다. 또는 하나의 피연산자만 다른 피연산자와 동일한 형식 및 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 개발 환경에서 이 컴파일러 옵션을 설정하려면

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

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

  3. 추가 옵션 속성을 수정하여 포함 /Zc:ternary 하거나 /Zc:ternary- 선택한 다음 확인을 선택합니다.

참고 항목

/Zc (규칙)