NeuroWhAI의 잡블로그

[C++] ADL(Argument Dependent Lookup) 혹은 Koenig Algorithm 설명 본문

개발 및 공부/언어

[C++] ADL(Argument Dependent Lookup) 혹은 Koenig Algorithm 설명

NeuroWhAI 2018. 6. 23. 15:21




ADL은 말 그대로 인수에 의존해 이름 공간을 검색하는 기능입니다.
긴 말 필요없고 코드부터 봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <algorithm>
 
namespace foo
{
    struct Bar
    {
        int data;
    };
    
    void init(Bar& bar)
    {
        bar.data = 42;
    }
}
 
int main()
{
    //using foo::init;
    //using namespace foo;
 
    foo::Bar bar;
    init(bar);
    
    std::cout << bar.data << std::endl;
 
    return 0;
}
cs
실행 결과 : https://ideone.com/pmuf7U

컴파일도 잘 되고 결과도 42로 잘 나옵니다.
그런데 ADL을 몰랐던 사람은 이 코드가 이상하다고 생각해야 합니다.
왜냐하면 init 함수는 foo라는 namespace 안에 있으므로 foo::Bar처럼 foo::init이라고 해줘야 합니다.
아님 주석친 부분처럼 using을 사용하거나 해야하는데 없어도 잘 됩니다.
이유는 짐작하셨듯이 ADL이 있기 때문입니다.

컴파일러는 init(bar)에서 bar가 Bar 클래스의 객체이고 Bar는 foo에 있다는걸 알 수 있으므로
init을 foo에서도 찾아보게 됩니다.
찾아보니 있었으므로 foo::init을 호출하게 되는 것입니다.

이 기능은 프로그래머에게 편리함을 더해주지만 때론 찾기 힘든 버그를 발생시킬 우려가 있다고 합니다.
왜냐하면 아래 예시처럼 ADL이 우선시되는 상황 때문입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <algorithm>
 
namespace foo
{
    struct Bar
    {
        int data;
    };
    
    void swap(Bar& a, Bar& b)
    {
        std::cout << "foo::swap" << std::endl;
    
        int temp = a.data;
        a.data = b.data;
        b.data = temp;
    }
}
 
int main()
{
    foo::Bar bar1, bar2;
    
    bar1.data = 42;
    bar2.data = 777;
    
    std::cout << bar1.data << ' ' << bar2.data << std::endl;
    
    using std::swap;
    swap(bar1, bar2); // Call foo::swap not std::swap.
    
    std::cout << bar1.data << ' ' << bar2.data << std::endl;
 
    return 0;
}
cs
실행 결과 : https://ideone.com/K4UAzr

using std::swap;
swap(bar1, bar2);
는 직관적으로 보면 std의 swap을 호출할 것 같지만
bar1, bar2의 클래스인 Bar가 속한 foo에 swap이 있으므로 여기의 swap이 호출됩니다.
(물론 std::swap(...) 형식으로 명시해주면 std의 swap이 호출되긴 합니다.)

심지어 아래 코드의 경우 굳이 안찾아봐도 될 foo::init을 보곤 전역 공간의 init과 모호(ambiguous)하다며 컴파일 오류를 뱉기도 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <algorithm>
 
namespace foo
{
    struct Bar
    {
        int data;
    };
    
    void init(Bar& bar)
    {
        bar.data = 42;
    }
}
 
void init(foo::Bar& bar)
{
    bar.data = 777;
}
 
int main()
{
    foo::Bar bar;
    init(bar) // Error! call of overloaded 'init(foo::Bar&)' is ambiguous.
    
    std::cout << bar.data << std::endl;
 
    return 0;
}
cs
컴파일 결과 : https://ideone.com/V02eBp

using foo::init;이나
using namespace foo;가 있었다면 이해가 되는 오류지만
없는데도 ADL 때문에 오류가 발생하는겁니다.

편한 만큼 감수해야할 부분이 있다고 보면 되겠네요.

Koenig Algorithm이라고도 부르는 이유는 이 알고리즘을 고안한 사람 이름이 Koenig이기 때문이라고 하네요.

그럼 이만!





Comments