C ++ 템플릿 오류 메시지가 왜 그렇게 끔찍한가요? 길고 읽을 수없는 오류

C ++ 템플릿은 길고 읽을 수없는 오류 메시지를 생성하는 것으로 유명합니다. C ++의 템플릿 오류 메시지가 왜 그렇게 나쁜지에 대한 일반적인 아이디어가 있습니다. 본질적으로 문제는 컴파일러에서 템플릿의 특정 유형이 지원하지 않는 구문을 발견 할 때까지 오류가 발생하지 않는다는 것입니다. 예를 들면 다음과 같습니다.

template <class T>
void dosomething(T& x) { x += 5; }

경우 T지원하지 않는 +=연산자를, 컴파일러는 오류 메시지를 생성합니다. 그리고 이것이 어딘가에 라이브러리 내에서 발생하면 오류 메시지의 길이는 수천 줄이 될 수 있습니다.

그러나 C ++ 템플릿은 본질적으로 컴파일 타임 덕 타이핑을위한 메커니즘 일뿐입니다. C ++ 템플릿 오류는 개념적으로 Python과 같은 동적 언어에서 발생할 수있는 런타임 유형 오류와 매우 유사합니다. 예를 들어 다음 Python 코드를 고려하십시오.

def dosomething(x):
   x.foo()

여기에 메소드 x가 없으면 foo()Python 인터프리터는 예외를 발생시키고 문제를 나타내는 매우 명확한 오류 메시지와 함께 스택 추적을 표시합니다. 인터프리터가 일부 라이브러리 함수에 깊숙이 들어갈 때까지 오류가 발생하지 않더라도 런타임 오류 메시지는 여전히 전형적인 C ++ 컴파일러가 내 보낸 읽을 수없는 구토만큼 나쁘지 않습니다. 그렇다면 왜 C ++ 컴파일러가 무엇이 잘못되었는지 더 명확하게 알 수 없습니까? 일부 C ++ 템플릿 오류 메시지가 문자 그대로 콘솔 창을 5 초 이상 스크롤하는 이유는 무엇입니까?



답변

템플릿 오류 메시지는 악명 높을 수 있지만 항상 길고 읽을 수있는 것은 아닙니다. 이 경우 전체 오류 메시지 (gcc)는 다음과 같습니다.

test.cpp: In function void dosomething(T&) [with T = X]’:
test.cpp:11:   instantiated from here
test.cpp:6: error: no match for operator+=’ in x += 5

Python 예제에서와 같이 템플릿 인스턴스화 지점의 “스택 추적”과 문제를 나타내는 명확한 오류 메시지가 표시됩니다.

여러 가지 이유로 템플릿 관련 오류 메시지가 더 길어질 수 있습니다.

  • “스택 추적”이 훨씬 더 깊을 수 있습니다.
  • 템플릿이 다른 템플릿 인스턴스화를 인수로 인스턴스화하고 모든 네임 스페이스 한정자와 함께 표시되므로 유형 이름이 훨씬 길어질 수 있습니다.
  • 과부하 해결에 실패하면 오류 메시지에 후보 과부하 목록 (각각 매우 긴 유형 이름이 포함될 수 있음) 목록이 포함될 수 있습니다.
  • 여러 곳에서 잘못된 템플릿을 인스턴스화하면 동일한 오류가 여러 번보고 될 수 있습니다.

파이썬과의 주요 차이점은 정적 유형 시스템이므로 오류 메시지에 (때로는 긴) 유형 이름을 포함시켜야합니다. 그것들이 없으면, 왜 그런지 진단하기가 매우 어려울 것입니다 과부하 해결이 실패한 입니다. 그들과 함께, 당신의 도전은 더 이상 문제의 위치를 ​​추측하는 것이 아니라 문제의 위치를 ​​알려주는 상형 문자를 해독하는 것입니다.

또한 런타임에 확인하면 첫 번째 오류가 발생하면 프로그램이 중지되고 단일 메시지 만 표시됩니다. 컴파일러는 포기할 때까지 발생한 모든 오류를 표시 할 수 있습니다. 적어도 C ++에서는 파일의 첫 번째 오류에서 멈추지 않아야합니다. 이는 이후 오류의 결과 일 수 있기 때문입니다.


답변

몇 가지 명백한 이유는 다음과 같습니다.

  1. 역사. gcc, MSVC 등이 새로워 졌을 때 더 많은 오류 메시지를 생성하기 위해 데이터를 저장하기 위해 많은 추가 공간을 사용할 여유가 없었습니다. 그들이 할 수 없었던 기억은 부족했다.
  2. 몇 년 동안 소비자는 오류 메시지 품질을 무시했기 때문에 공급 업체도 대부분 그렇게했습니다.
  3. 일부 코드를 사용하면 컴파일러는 나중에 코드의 실제 오류를 다시 동기화하고 진단 할 수 있습니다. 템플릿의 오류가 너무 심각하여 첫 번째 이후의 항목은 거의 항상 쓸모가 없습니다.
  4. 템플릿의 일반적인 유연성은 아마도 당신이 무엇을 추측하기 어렵게 만듭니다 코드에 오류가있을 때 의미했다.
  5. 템플릿 내부의 이름의 의미는 템플릿의 컨텍스트 및 인스턴스화의 상황 모두에 의존 하고 종속 조회가 여전히 더 많은 가능성을 추가 할 수 있습니다 인수입니다.
  6. 함수 오버로딩은 특정 함수 호출에 대한 많은 후보를 제공 할 수 있습니다. 참조 , 모호한 경우 일부 컴파일러 (예 : gcc)가이 하게 나열합니다.
  7. 전달 된 값이 요구 사항을 충족한다고 확신하지 않고 일반 매개 변수 사용을 고려하지 않은 많은 코더는 템플릿 매개 변수를 전혀 확인하려고 시도조차하지 않습니다 (고백해야합니다.

그것은 철저하지는 않지만 일반적인 아이디어를 얻습니다. 쉽지는 않더라도 대부분 치료할 수 있습니다. 몇 년 동안 저는 사람들에게 Comeau C ++ 사본을 정기적으로 사용하라고 지시했습니다. 컴파일러를 지불하기 위해 한 번의 오류 메시지에서 한 번만 충분히 저장했을 것입니다. 이제 Clang은 같은 시점에 도달하고 있습니다 (그리고 훨씬 저렴합니다).

나는 농담처럼 들리지만 실제로는 그렇지 않은 일반적인 관찰로 마무리 할 것입니다. 대부분의 경우, 컴파일러의 진짜 직업은 정직 하다 오류 메시지에 소스 코드를 켭니다. 벤더가 그 일을 좀 더 잘하는 데 시간이 많이 걸린다. 비록 컴파일러를 작성할 때 나는 그것을 2 차 (최고)로 취급하는 경향이 있고 어떤 경우에는 거의 무시한다는 것을 공개적으로 인정할 것이다. 완전히.


답변

간단한 대답은 파이썬이 그런 식으로 작동하도록 설계 되었기 때문에 템플릿과 관련된 많은 것들이 우연히 생겨났습니다. 예를 들어 튜링 완성 시스템이 될 의도는 없었습니다. 시스템이 작동 할 때 어떤 일이 발생하는지 계획적으로 계획하고 추론 할 수없는 경우 왜 누군가가 잘못 될 때 발생하는 일에 대해 신중하고 신중하게 계획해야합니까?

또한 지적했듯이 Python 인터프리터는 Python 코드를 해석하기 때문에 스택 추적을 표시하여 훨씬 쉽게 만들 수 있습니다. C ++ 컴파일러가 템플릿 오류를 발견하고 스택 추적을 제공한다면 “템플릿 구토”만큼 도움이되지 않습니까?