작성
·
148
·
수정됨
1
강의 내용 22:00에 보편참조라고 설명해 주시면서
Job 생성자를 만들어주셨습니다.
하지만 Job생성자는 우측값 참조가 아니면 생성이 되지 않습니다.
반면에 우측값 참조로 넣어주면 전부 에러 없이 생성되었습니다
Job 생성자가 보편참조라면 좌측값인 player로도 생성이 되어야 하지 않나요?
만약 Job 생성자가 보편참조가 아니라면
GRoom->PushJob(&Room::Enter, player) 부분에서
보편참조가 아닌 함수에서 L-value를 std::forward를 사용하면 오른쪽 참조값으로 바뀌기 때문에
auto job = ObjectPool<Job>::MakeShared(owner, memFunc, std::forward<Args>(args)...);
이함수는 결국 우측값 참조를 받는 Job생성자를 호출하게 되어 Job을 만들어 주었기 때문에 에러가 발생하지 않고 잘 실행 되었던거라고 이해하면 될까요?
만약 Job생성자가 보편참조가 아니라면 왜 보편참조가 될 수 없나요?
강의 핵심내용은 JobQueue에 관한 내용이지만
보편참조가 너무 헷갈려서 질문하게 되었습니다..
답변 1
0
저도 같은 부분에 궁금증을 가져서 삽질해본 결과 그나마 합리적인 추측을 할 수 있었습니다.
1. Job 생성자의 마지막 인자 Args&&... arg는 보편 참조가 아닙니다. 그 이유는 앞선 두 번째 인자인 Ret(T::* memFunc)(Args...) 에서 Args가 먼저 추론되고 이후 마지막 인자인 Args는 추론이 되지 않습니다. (non-deduced) 즉, 이미 추론된 타입에 &&가 붙게 됩니다. 이 말은 마지막 인자는 항상 rvalue reference가 된다는 의미이죠.
2. std::forward는 템플릿 인자 T에 참조가 붙지 않으면 rvalue로 캐스팅하여 리턴해줍니다.
PushJob의 정의를 보면 Args는 보편참조가 아닌 일반 lvalue만을 받는 템플릿 형식입니다. 여기서 받은 비참조 타입 lvalue를 forward를 통해 rvalue로 캐스팅한 후 MakeShared에 넘겨주고, 이 안에서는 계속 perfect forwarding을 통해 값 카테고리를 보존한 채 placement new(생성자 호출)까지 도달하게 됩니다.
아마 테스트에서 std::forward<PlayerRef&>(player)를 Job 생성자에 바로 넣어주면 똑같이 컴파일 에러가 나올겁니다.
3. 1번의 답변과 일맥상통 할 것 같습니다.
저는 이와 같이 이해하고 넘어가려 하며 정확하지 않은 정보일 수 있으니 더 정확한 정보를 위해서는 cppreference의 템플릿 추론 규칙 아티클이나 모던 c++의 템플릿 추론 챕터를 읽어보는 것을 추천드립니다!