ODR: 멤버 대 비멤버 함수

저는 항상 C++에서 inline 키워드가 혼란스럽고 제대로 설계되지 않은 개념임을 발견했습니다. 다음은 ODR(one-definition-rule)을 준수하도록 설계된 인라인에 대한 추가 사용 사례입니다.

일반적인 C++ 작업은 스트림 삽입 연산자를 오버로드하여 표현식 output stream << <instance of my type>;을 사용하여 사용자 정의 유형을 인쇄할 수 있도록 하는 것입니다. 이러한 오버로드에 대한 올바른 정의는 다음과 같습니다.

#ifndef FOO_H
#define FOO_H

#include <iostream>


class Foo {
    public:
        Foo() { m_id = 0; }
    private:
        int m_id;

    friend std::ostream& operator<<(std::ostream& os, Foo& f);

};

std::ostream& operator<<(std::ostream& os, const Foo& f) {
    os << f.m_id << std::endl;
    return os;
} 


#endif


이제 소스 파일에서 cout << Foo() 을 수행하면 컴파일러는 위의 friend 함수 오버로드에 대한 호출을 기꺼이 해결합니다.

그러나 두 개의 서로 다른 소스 파일에 Foo.h를 포함하면 어떻게 될까요? operator << (ostream&, const Foo&) 함수에 대해 두려운 다중 정의 오류가 발생합니다.

이를 수정하는 두 가지 방법이 있습니다. Foo.cpp를 만들고 operator << (ostream&, const Foo&)의 정의를 해당 소스 파일로 이동한 다음 나머지 프로그램으로 해당 소스 파일을 빌드하는 것입니다. 다른 옵션은 헤더 파일에 함수 정의를 유지하되 아래와 같이 키워드 inline을 추가하는 것입니다.

inline std::ostream& operator<<(std::ostream& os, const Foo& f) {
    os << f.m_id << std::endl;
    return os;
} 

inline 키워드의 존재는 이 함수가 사용되는 모든 번역 단위(소스 파일)의 헤더 파일에 있는 이 정의를 사용하도록 컴파일러에 지시합니다.

본질적으로 헤더 파일에 정의된 함수가 있는 경우 인라인 함수가 되는 것이 좋습니다. 그렇지 않으면 여러 소스 파일에 포함된 경우 ODR 규칙에 위배되는 해당 함수의 정의가 여러 개 있을 것입니다.

이제 헤더 파일에 정의된 멤버 함수에는 왜 같은 것이 적용되지 않는지 궁금할 것입니다.

예를 들어 아래와 같이 getId() 클래스에 Foo이라는 새 멤버 함수를 추가한다고 가정해 보겠습니다.

#ifndef FOO_H
#define FOO_H

#include <iostream>


class Foo {
    public:
        Foo() { m_id = 0; }
        int getId() { return m_id; }
    private:
        int m_id;

    friend std::ostream& operator<<(std::ostream& os, Foo& f);

};

inline std::ostream& operator<<(std::ostream& os, const Foo& f) {
    os << f.m_id << std::endl;
    return os;
} 


#endif


이제 getId()을 호출하는 두 번역 단위가 getId()의 두 가지 정의를 얻는지 궁금할 것입니다. operator <<(ostream&, const Foo&)Foo.h이 정의되어 있고 두 개의 소스 파일에 Foo.h이 포함되어 있을 때 동일한 일이 발생했기 때문입니다.

그러나 이것은 사실이 아닙니다. Foo.h 을 원하는 수의 소스 파일에 포함할 수 있으며 모두 getId() 에 정의된 Foo.h 의 단일 정의로 해석됩니다. 왜 이런거야? 무한한 지혜로 C++ 디자이너는 클래스 정의에 정의된 모든 멤버 함수를 암시적으로 인라인으로 선택합니다.

암시적으로 인라인된 함수가 호출될 때 실제로 인라인되는지 여부는 아직 모르겠습니다. "인라인은 힌트일 뿐이며 컴파일러는 이를 무시할 수 있습니다"라는 C++ 격언이 있습니다. 암시적으로 인라인된 함수에 적용됩니까? 다시 말해, 클래스 정의에 정의된 함수에 대해 500줄 정의를 붙여도 컴파일러가 여전히 인라인 처리합니까?

당신은 그것을 가지고 있습니다. inline 키워드가 혼란스럽다면 혼자가 아닙니다. 언어 기능을 그렇게 직관적이지 않게 만드는 것은 나에게 의미가 없습니다.

좋은 웹페이지 즐겨찾기