**One Definition Rule (ODR)**는 C++의 근본적인 규칙으로, 프로그램 전체(모든 translation unit)에서 각 객체, 함수 또는 클래스에 대해 정확히 하나의 정의(implementation)가 있어야 한다는 요구 사항입니다.
ODR은 다음과 같은 경우에 위반됩니다:
이로 인해 잡기 힘든 예기치 않은 버그, 잘못된 링크 또는 더 심각하게는 프로그램의 동작이 컴파일 방법에 따라 달라지는 상황이 발생합니다.
왜 위반되는가:
대형 프로젝트에서는 종종 .h 파일을 버전 관리 없이 복사/수정 하거나, 코드를 여러 모듈로 분리하여 독립적으로 컴파일하는 경우가 많습니다. 누군가 인라인 함수를 한 곳만 변경하면 나머지 소스 파일은 오래된 버전을 가질 수 있습니다.
어떻게 피할 것인가:
constexpr 또는 C++17에서 inline 변수를 사용하십시오.서로 다른 .cpp 파일에서 같은 이름과 서로 다른 구현을 가진 static 함수(static void foo())를 정의해도 문제가 없습니까?
많은 사람들은 static 함수가 모듈 간에 서로 영향을 미치지 않다고 생각합니다. 답은: 예, 가능합니다. 각 함수는 내부 링크(자신의 translation unit 내에서만 표시됨)를 가지므로 그렇습니다. 그러나 인라인 함수와 템플릿에 대해서는 이러한 보장이 되지 않으며, 이는 종종 혼동됩니다.
예제:
// file1.cpp static void foo() { std::cout << "A"; } // file2.cpp static void foo() { std::cout << "B"; }
이러한 모듈에서의 호출은 독립적입니다.
이야기
대형 프로젝트에서 한 개발자가 .h 파일의 클론 중 하나에서만 인라인 함수의 본문을 변경했습니다. 빌드 후 일부 동작이 예측할 수 없게 되었고, 일부 모듈은 이전 알고리즘을 사용하고 다른 모듈은 새로운 알고리즘을 사용했습니다. 원인은 인라인 함수에 대한 ODR 위반이었습니다.
이야기
C++17로 마이그레이션할 때, 상수 변수가 여러 헤더 파일에서 inline 키워드 없이 정의되었습니다. 링크에서 중복 기호 오류가 다수 발생했습니다. 이를 수정하기 위해 'inline const'로 변수를 선언했습니다.
이야기
생성된 .h 파일에 동일한 템플릿 클래스의 메서드에 대한 두 개의 구현이 서로 다른 빌드에서 ifdef 검사로 구별되어 포함된 것이 늦게 발견되었습니다. 이후 애플리케이션이 관련 모듈 간 ABI 불일치로 인해 주기적으로 크래시되었습니다.