Notice
Recent Posts
Recent Comments
NeuroWhAI의 잡블로그
[C++] ADL(Argument Dependent Lookup) 혹은 Koenig Algorithm 설명 본문
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 |
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 |
using foo::init;이나
using namespace foo;가 있었다면 이해가 되는 오류지만
없는데도 ADL 때문에 오류가 발생하는겁니다.
편한 만큼 감수해야할 부분이 있다고 보면 되겠네요.
Koenig Algorithm이라고도 부르는 이유는 이 알고리즘을 고안한 사람 이름이 Koenig이기 때문이라고 하네요.
그럼 이만!
'개발 및 공부 > 언어' 카테고리의 다른 글
[Rust] Auto-Dereferencing Rules (자동 역참조 규칙) (0) | 2018.07.27 |
---|---|
[Rust] &mut T -> &T 변경하기 (0) | 2018.07.27 |
[JavaScript] replace all (0) | 2018.06.19 |
[C#] BinaryFormatter 사용해서 직렬화/역직렬화 (0) | 2018.06.03 |
[Rust] ? 연산자 (0) | 2018.06.03 |
Comments