유튜브 알고리즘에 낚여서 또 CppCon 영상을 봤다..
https://www.youtube.com/watch?v=1hwRxW99lg0
분명 첫 코드까지만 해도 아무생각이 없었다
auto main() -> int {
return 0;
}
// Result: 0
근데 바로 뒤에 이게 등장했다
auto main() -> int {
return -(-![]{});
}
// Result: 0
네?..선생님?..어째서 저게 0인거죠?
그리고 바로 다음 장면에서는 저걸로 1을 만든다
auto main() -> int {
return (-(-![]{}))-(-!(![]{}));
}
// Result 1
도저히 모르겠어서 요즘 핫하다는 Chat-GPT한테 물어봤다
initializer list 어쩌구하면서 헛소리를 정성스럽게 한다
아니 저거 lambda expression이잖아..
valid하지 않은 문법이란다
아니 방금 돌려보고 왔는데 무슨 말도 안되는
그 다음엔 무슨 Undefined Behavior라고 한다
이 새끼 컴파일러 탓도하네..
Chat-GPT에게서 원하는 대답을 얻을 수 없다는걸 깨닫고 구글링을 해보았다
근데 안나온다..
그래서 그냥 무지성으로 Debugging하면서 해석해보았다
auto main() -> int {
auto lambda = []{};
}
위에 적힌 저 Expression이 Lambda인 것은 자명한 사실이다
그런데 저기에 not operator!
을 붙인다면?
auto main() -> int {
int some_integer = ![]{};
}
갑자기 정수형이 된다
그리고 심지어 저 값은 0이다
auto main() -> int {
int zero = ![]{};
return zero;
}
// Result: 0
그때 무심코 한가지 개념이 머리속을 스쳐지나갔다
Lambda는 Capture가 없을 경우, funtion pointer로 변환이 가능합니다
어디에 나와있는지 찾기 귀찮음
Pointer에 not operator!
을 취하면 0(NULL)이 된다
auto foo() -> void {}
auto main() -> int {
void (*ptr)() = foo;
int zero_from_ptr = !ptr;
return zero_from_ptr;
}
// Result: 0
이제 모든게 이해가 된다
덕지덕지 붙어있는 Symbol을 걷어내고 나면 결국 나오는건 Pointer다
이제 아래와 같은 단계를 거치면 모든 정수를 얻을 수 있다
!
을 취해 0으로 만든다!
을 취해 1로 만든다이해를 했다면 이제 검증을 해보자
cpp 개발자의 필수 교양 godbolt.org에 아래 코드를 넣고 결과를 뽑아봤다
GCC 13.1 --std=c++2b -O0 -march=skylake
auto main() -> int {
int zero_from_lambda = ![]{};
}
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
mov eax, 0
pop rbp
ret
Optimization이 없는데도 바로 0으로 만들어버린다
이제 function pointer
에서도 동일한 결과가 나오는지 확인해보자
GCC 13.1 --std=c++2b -O0 -march=skylake
auto foo() -> void {}
auto main() -> int {
int zero_from_func_ptr = !(&foo);
}
foo():
push rbp
mov rbp, rsp
nop
pop rbp
ret
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
mov eax, 0
pop rbp
ret
완벽하게 동일한 결과가 나온다
우리가 적은 code는 mov DWORD PTR [rbp-4], 0
부분이다
즉, lambda가 function pointer
와 같이 바뀌었음을 확인할 수 있다
원래부터 있던 기능인지는 모르겠으나 언젠가부터 위 내용은 Warning으로 취급되기 시작했다
정확히는 -Wpointer-bool-conversion 옵션이니 찾아보면 좋을듯하다
이제 아래와 같은 괴상한 코드를 넣으면 컴파일러가 친절하게 혼내준다
https://godbolt.org/z/WfbaTKcKK
auto main() -> int {
auto zero = -(-![]{});
auto one = !(-(-![]{}));
auto two = (!(-(-![]{})))-(-!(-(-![]{})));
return two + one;
}
/* Output
<source>:2:21: warning: address of lambda function pointer conversion operator will always evaluate to 'true' [-Wpointer-bool-conversion]
2 | auto zero = -(-![]{});
| ~^
<source>:3:22: warning: address of lambda function pointer conversion operator will always evaluate to 'true' [-Wpointer-bool-conversion]
3 | auto one = !(-(-![]{}));
| ~^
<source>:4:39: warning: address of lambda function pointer conversion operator will always evaluate to 'true' [-Wpointer-bool-conversion]
4 | auto two = (!(-(-![]{})))-(-!(-(-![]{})));
| ~^
<source>:4:23: warning: address of lambda function pointer conversion operator will always evaluate to 'true' [-Wpointer-bool-conversion]
4 | auto two = (!(-(-![]{})))-(-!(-(-![]{})));
| ~^
4 warnings generated.
ASM generation compiler returned: 0
<source>:2:21: warning: address of lambda function pointer conversion operator will always evaluate to 'true' [-Wpointer-bool-conversion]
2 | auto zero = -(-![]{});
| ~^
<source>:3:22: warning: address of lambda function pointer conversion operator will always evaluate to 'true' [-Wpointer-bool-conversion]
3 | auto one = !(-(-![]{}));
| ~^
<source>:4:39: warning: address of lambda function pointer conversion operator will always evaluate to 'true' [-Wpointer-bool-conversion]
4 | auto two = (!(-(-![]{})))-(-!(-(-![]{})));
| ~^
<source>:4:23: warning: address of lambda function pointer conversion operator will always evaluate to 'true' [-Wpointer-bool-conversion]
4 | auto two = (!(-(-![]{})))-(-!(-(-![]{})));
| ~^
4 warnings generated.
Execution build compiler returned: 0
Program returned: 3
*/
그리고 한가지 더 바뀐점이 있다면 바로 ChatGPT이다
지금까지 무수히 많은 대화형 AI한테 동일한 질문을 했었지만 올바른 대답은 얻지 못했다
그런데 문득 생각이 나서 4o 버전에 다시 넣어보았더니 드디어 올바른 대답을 한다
우리 아이가 드디어 달라졌어요