백마법

&(const T&)T(...)

프로필사진
fienestar
2023. 11. 9. 14:46

 

#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