#include <iostream>
int main()
{
std::cout << (const int(&)[10]){1, 2, 3}[2]; // ?????
}
g++(8.3.1) output: 3
누군가 포인터와 참조의 차이를 물었는데, 가장 큰 차이는 nullable 여부이고 참조는 좀 더 기능이 많다는 식으로 말했다. (*(int*)nullptr은 ub다)
문득 든 생각이, reference to const는 주소가 없는 값도 허용한다. 주로 복사를 피하면서 주소가 있든 없든 받기 위해 사용되는데, 다음과 같은 식이다.
void f(const T& value)
{
...
}
int main()
{
f(T());
T a;
f(a);
}
그런데 이런 implicit conversion이 허용된다는 말은, 캐스팅해도 된다는 말이 아닐까? 그럼 변수도, 함수도 없이 참조 가능한 주소가 발생하는데, 이게 컴파일이 될까?
std::cout << &(const int&)0;
잘 된다. 돌이켜보면 void f(const T&)도 컴파일되고나면 함수에서는 주소값만 받는데, 당연히 이 주소는 피호출자(void f(const T&))가 생성하지 않는다. 리터럴을 위한 별도의 helper또한 존재하지 않는다.
어셈을 까보면(g++ 8.3.1)
mov DWORD PTR [rbp-4], 0
즉, g++은 로컬 변수를 만든것 처럼 컴파일하고 있었다. 아래 소스와 동일하게 컴파일되었다.
int temp = 0;
std::cout << &temp;
무조건 const이라는 점과, storage specifier를 사용할 수 없다는 점을 제외하면 C의 compound literal((int){0})과 유사하다.
활용 가능한 영역을 생각해보면 다음과 같다.
void f(const int*); // some function with nullable pointer parameter
f(&(const int&)127);
배열, 클래스 등은 다음과 같이 생성 가능하다.
// using initializer_list
(const int(&)[3]){1, 2}
(const T&){...}
// using non-explicit constructor
(const T&) value
// using constructor
(const T&)T(...)
+) g++에서 (const int&)0은 stack(automatic storage)에 있었던 반면 (const int(&)[10]){1, 2, 3}은 code 영역에 있었다.
._anon_91:
.long 1
.long 2
.long 3
.zero 28
'백마법' 카테고리의 다른 글
설치 usb 없이 빈 디스크에 os 설치하기 (0) | 2024.03.13 |
---|