삶의 공유

C++ ADL (Argument Dependent Lookup) 완벽 정리 본문

Programing언어/C++

C++ ADL (Argument Dependent Lookup) 완벽 정리

dkrehd 2025. 10. 15. 21:48
728x90
반응형

📘 C++ ADL (Argument Dependent Lookup) 완벽 정리

C++을 배우다 보면 draw_pixel(pt); 처럼 namespace를 생략했는데도 함수가 잘 호출되는 걸 볼 수 있습니다.
“아니, Graphics::draw_pixel()이 아닌데 왜 되는 거지?”
그 이유가 바로 ADL(Argument Dependent Lookup), 즉 인자 종속 이름 탐색(또는 Koenig Lookup) 입니다.

이 문법은 C++에서 함수 검색 방식에 매우 중요한 역할을 합니다.


🧩 1. ADL이란?

함수 호출 시, 인자(argument)의 타입이 속한 네임스페이스(namespace)를 자동으로 탐색에 포함시키는 규칙
→ Koenig Lookup이라고도 불립니다.

즉, 함수의 이름만 보고 찾는 게 아니라
“이 함수의 인자가 어디 소속인지”를 기준으로 추가적인 탐색 범위가 자동으로 확장됩니다.


🎯 2. 예제로 이해하는 ADL

 
 
namespace Graphics {
    class Point {};

    void draw_pixel(const Point& p1) {
        std::println("Draw pixel in Graphics namespace");
    }

    void set_color(int c) {}
}

int main() {
    Graphics::Point pt;

    // 명시적 호출
    Graphics::draw_pixel(pt);

    // ADL 덕분에 이렇게도 가능!
    draw_pixel(pt);
}
 

🔍 실행 결과

 
Draw pixel in Graphics namespace
Draw pixel in Graphics namespace
 

💡 설명

  • pt는 Graphics::Point 타입입니다.
  • 컴파일러는 draw_pixel(pt)를 찾을 때,
    1. 현재 스코프(전역 등)에서 draw_pixel을 찾고,
    2. pt가 속한 네임스페이스(Graphics)도 탐색 대상에 포함합니다.
  • 결국 Graphics::draw_pixel()을 찾아 호출합니다.

즉, draw_pixel(pt) → Graphics::draw_pixel(pt) 로 자동 연결됩니다.
이 동작이 바로 ADL 입니다.


🧠 3. ADL이 만들어진 이유

ADL이 없다면, 다음과 같은 불편한 코드가 되어버립니다.

 
std::string s1, s2;
s1 + s2;          // ✅ 실제로는 잘 동작함 (ADL 덕분)
 
 

하지만 ADL이 없다면 이렇게 써야 합니다:

std::operator+(s1, s2);   // ❌ 너무 장황하고 비직관적
 
 

즉, ADL은 연산자 오버로딩을 자연스럽게 사용할 수 있게 해주는 핵심 문법입니다.
표준 라이브러리(std::string, std::vector, std::swap 등)에서도 ADL이 광범위하게 사용됩니다.


🧩 4. ADL이 동작하는 예: std::string 연산자

 
namespace std {
    class string {
        // 실제 구현은 훨씬 복잡하지만 예시를 위해 단순화
    };

    string operator+(const string& s1, const string& s2) {
        return s1; // (단순 예시용)
    }
}

int main() {
    std::string s1, s2;
    s1 + s2;  // ✅ 가능 — ADL이 std namespace를 탐색에 포함
}
 
 

💡 설명

  • s1과 s2는 std::string 타입입니다.
  • 컴파일러가 operator+를 찾을 때
    → std 네임스페이스를 자동으로 탐색 범위에 추가
  • 그 결과 std::operator+ 를 찾아 호출하게 됩니다.

ADL이 없다면 s1 + s2 는 컴파일 에러가 납니다.


⚙️ 5. std::swap 예시로 보는 ADL의 한계

 
#include <algorithm>
#include <string>

int main() {
    int n1 = 10;
    int n2 = 20;

    std::string s1 = "AAA";
    std::string s2 = "BBB";

    std::swap(n1, n2); // ✅ 명시적으로 std::swap 호출
    std::swap(s1, s2); // ✅ std::string은 std::swap이 있음

    swap(s1, s2); // ✅ ADL 작동 — string은 std 네임스페이스 타입
    swap(n1, n2); // ❌ 에러 — int는 std에 속하지 않음
}
 
 

💬 해설

타입네임스페이스ADL 탐색 대상결과
std::string std std::swap 탐색 성공 ✅ OK
int 전역 타입 없음 (네임스페이스 X) ❌ 컴파일 에러

👉 기본 타입(int, double 등) 은 네임스페이스에 속하지 않기 때문에
ADL로 탐색할 공간이 없습니다.
따라서 swap(n1, n2)는 실패하지만, swap(s1, s2)는 성공합니다.


⚙️ 6. 실무에서의 권장 사용법

C++ 표준 함수(std::swap, std::sort, std::begin 등)는 가독성을 위해 std::를 붙이는 것이 권장됩니다.

즉,

 
std::swap(x, y);     // ✅ 명시적 호출 권장
swap(x, y);          // ✅ ADL 작동은 하지만 가독성 ↓
그리고 템플릿 함수 내부에서는 다음 패턴이 가장 안전합니다.
 
using std::swap;
swap(a, b);  // ✅ 사용자 정의 타입이면 ADL로, 아니면 std::swap 호출
 

이 패턴은 표준 라이브러리에서도 자주 사용됩니다.


🔎 7. ADL의 핵심 동작 요약

항목설명
이름 탐색 방식 인자의 타입이 속한 네임스페이스를 자동 탐색
적용 대상 일반 함수, 연산자 함수
효과 namespace:: 생략 가능
대표 예시 std::string의 operator+, std::swap
주의사항 기본 타입(int 등)은 ADL이 적용되지 않음
권장사항 std::swap 등은 가독성을 위해 std::를 붙이는 습관

📌 핵심 요약

  • ADL (Argument Dependent Lookup) = “함수 인자의 네임스페이스를 자동 탐색하는 규칙”
  • 이유: 연산자 오버로딩과 네임스페이스 분리를 자연스럽게 연결하기 위해
  • 효과: draw_pixel(pt) → Graphics::draw_pixel(pt) 자동 인식
  • 주의: int, double 같은 기본형은 ADL 대상이 아님
  • 실무 팁: 표준 함수는 std::를 명시적으로 쓰는 게 더 안전하고 명확함
반응형