다음을 통해 공유


문자열 리터럴

업데이트: 2007년 11월

Visual C++ 2008에서는 문자열 리터럴 처리 방법이 Managed Extensions for C++와 다르게 변경되었습니다.

Managed Extensions for C++ 언어 디자인에서 관리되는 문자열 리터럴은 문자열 리터럴 앞에 S를 추가하여 표시했습니다. 예를 들면 다음과 같습니다.

String *ps1 = "hello";
String *ps2 = S"goodbye";

다음 CIL 표현에서 ildasm을 통해 알 수 있듯이 두 초기화 방식 사이의 성능 오버헤드는 상당히 큰 차이를 보입니다.

// String *ps1 = "hello";
ldsflda    valuetype $ArrayType$0xd61117dd
     modopt([Microsoft.VisualC]Microsoft.VisualC.IsConstModifier) 
     '?A0xbdde7aca.unnamed-global-0'

newobj instance void [mscorlib]System.String::.ctor(int8*)
stloc.0

// String *ps2 = S"goodbye";
ldstr      "goodbye"
stloc.0

리터럴 문자열 앞에 접두사로 S를 추가한다는 점만 익혀 두면 됩니다. 새 구문의 경우에는 어떠한 상황에서 사용되는지에 따라 문자열 리터럴이 투명하게 처리됩니다. S는 이제 지정할 필요가 없습니다.

그렇다면 컴파일러에 특정 해석을 명시적으로 지정하려는 경우에는 어떻게 해야 할까요? 이러한 경우에는 명시적 캐스트를 적용합니다. 예를 들면 다음과 같습니다.

f( safe_cast<String^>("ABC") );

또한 문자열 리터럴을 String과 비교할 때 이제 표준 변환이 아닌 단순 변환이 사용됩니다. 그리 대단하지 않은 것처럼 보일지 모르지만 이는 유력한 정식 매개 변수로서의 String 및 const char*를 포함하여 오버로드된 함수 집합의 확인을 변경하는 결과를 가져옵니다. 이전에는 const char* 인스턴스로 확인되던 것이 이제는 모호한 것으로 플래그 지정됩니다. 예를 들면 다음과 같습니다.

ref struct R {
   void f(const char*);
   void f(String^);
};

int main () {
   R r;
   // old syntax: f( const char* );
   // new syntax: error: ambiguous
   r.f("ABC"); 
}

그 차이는 무엇 때문일까요? 프로그램 안에 f라는 인스턴스가 여러 개 있으므로 호출에 적용할 함수 오버로드 확인 알고리즘이 필요합니다. 오버로드 함수의 형식 확인은 세 단계로 진행됩니다.

  1. 후보 함수를 수집합니다. 후보 함수는 호출하려는 함수의 이름과 범위 내에서 글자 그대로 일치하는 메서드입니다. 예를 들어, R의 인스턴스를 통해 f()를 호출하므로 R 또는 해당 기본 클래스 계층 구조의 멤버가 아닌 f라는 함수는 모두 후보 함수가 아닙니다. 이 예제에서 후보 함수는 두 개입니다. 이 두 함수는 f라는 R의 멤버 함수입니다. 후보 함수 집합이 비어 있으면 이 단계에서 호출이 실패합니다.

  2. 후보 함수 중에서 사용 가능한 함수 집합을 선택합니다. 사용 가능한 함수는 지정된 인수 수와 해당 형식에 따라 호출에서 지정한 인수를 사용하여 호출할 수 있는 함수입니다. 이 예제에서는 두 후보 함수가 모두 사용 가능한 함수입니다. 사용 가능한 함수 집합이 비어 있으면 이 단계에서 호출이 실패합니다.

  3. 호출에 가장 완벽하게 일치하는 함수를 선택합니다. 이를 위해서는 인수를 사용 가능한 함수 매개 변수의 형식으로 변환하기 위해 적용되는 변환의 순위를 매깁니다. 단일 매개 변수 함수인 경우 이 작업은 비교적 쉽게 진행되지만 매개 변수가 여러 개인 경우에는 다소 복잡할 수 있습니다. 가장 완벽하게 일치하는 함수가 없으면 이 단계에서 호출이 실패합니다. 즉, 실제 인수의 형식을 정식 매개 변수의 형식으로 변환하는 데 필요한 변환이 우열을 가릴 수 없이 모두 적합한 경우 호출이 실패합니다. 이 호출은 모호한 것으로 플래그 지정됩니다.

Managed Extensions의 경우 이 호출을 확인하면 최상의 일치 항목으로 const char* 인스턴스가 호출됩니다. 새 구문에서는 "abc"를 const char* 및 String^에 일치시키는 데 필요한 변환이 서로 동등한 수준이므로 즉, 어느 쪽을 사용해도 동일한 결과를 얻을 수 있으므로 이 호출은 나쁜 것으로 즉, 모호한 것으로 플래그 지정됩니다.

여기서 두 가지 의문이 발생할 수 있습니다.

  • 실제 인수 "abc"의 형식은 무엇일까요?

  • 형식 변환 사이의 우열을 가리는 데는 어떠한 알고리즘이 사용되는 것일까요?

문자열 리터럴 "abc"의 형식은 const char[4]입니다. 모든 문자열 리터럴의 끝에는 암시적 null 종료 문자가 있습니다.

형식 변환 사이의 우열을 가리는 데 적용되는 알고리즘에서는 적용 가능한 형식 변환을 계층 구조로 배치합니다. 이 계층 구조에서 모든 변환은 물론 암시적입니다. 명시적 캐스트 표기법을 사용하면 괄호를 사용할 때 식의 일반적인 연산자 우선 순위가 재정의되는 것과 마찬가지로 계층 구조가 재정의됩니다.

  1. 정확하게 일치하는 항목이 있으면 가장 좋습니다. 의외일 수도 있겠지만 인수가 정확하게 일치하기 위해서 매개 변수 형식이 정확하게 일치할 필요는 없습니다. 매개 변수 형식은 어느 정도 비슷하기만 하면 됩니다. 이 예제에서 어떠한 일이 일어나고 있는지 이해하고 언어의 어떠한 부분이 변경되었는지 이해하기 위한 열쇠는 바로 여기에 있습니다.

  2. 확장이 표준 변환보다 더 좋습니다. 예를 들어, short int를 int로 확장하는 것이 int를 double로 변환하는 것보다 더 좋습니다.

  3. 표준 변환이 boxing 변환보다 더 좋습니다. 예를 들어, int를 double로 변환하는 것이 int를 Object로 boxing하는 것보다 더 좋습니다.

  4. boxing 변환이 암시적 사용자 정의 변환보다 더 좋습니다. 예를 들어, int를 Object로 boxing하는 것이 SmallInt 값 클래스의 변환 연산자를 적용하는 것보다 더 좋습니다.

  5. 암시적 사용자 정의 변환이 어떠한 변환도 전혀 없는 것보다 더 좋습니다. 암시적 사용자 정의 변환은 (형식 시그니처에의 해당 위치에 매개 변수 배열이나 가변 매개 변수(...)가 포함될 수 있음을 알리는) 오류를 제외한 마지막 보루입니다.

그렇다면 정확한 일치가 정확하게 일치하는 항목일 필요는 없다는 말의 의미는 무엇일까요? 예를 들어, const char[4]는 const char* 또는 String^과 정확하게 일치하지 않지만 이 예제의 모호성은 서로 충돌하는 두 개의 정확한 일치 항목 사이에 발생하고 있습니다.

정확한 일치에는 여러 가지 trivial 속성 변환이 포함되는 경우가 있습니다. ISO-C++에서 정확한 일치로 적용하고 한정할 수 있는 trivial 속성 변환에는 네 가지가 있습니다. 그 중 셋은 lvalue 변환이라고 하며, 네 번째 형식은 한정 변환이라고 합니다. 세 가지 lvalue 변환은 한정 변환을 필요로 하는 형식보다 더 정확한 일치로 취급됩니다.

lvalue 변환의 형식 중 하나는 네이티브 배열을 포인터로 변환하는 형식입니다. 이는 const char[4]를 const char*로 일치시키는 데 관련된 변환입니다. 따라서 f("abc")를 f(const char*)에 일치시키는 것은 정확한 일치입니다. 이전의 언어 버전에서 이는 실제로 최상의 일치였습니다.

따라서 컴파일러가 호출을 모호한 것으로 플래그 지정하려면 const char[4]를 String^으로 변환하는 일이 trivial 속성 변환을 통한 정확한 일치이기도 해야 합니다. 새 언어 버전에서 추가된 변경 사항은 바로 이 점입니다. 이제는 호출이 모호한 것으로 플래그 지정되는 이유도 여기에 있습니다.

참고 항목

개념

일반적인 언어 변경 사항

참조

System::String Handling in Visual C++