Loading
2018. 9. 14. 13:12 - lazykuna

[C++] template에서 rvalue reference로 파라미터 받기?

여분의 시간이 남아서 Modern C++ 기술을 좀 배워볼까~ 하면서 이리저리 보다가, STL 정의들이 하나같이 T&& 꼴로 인자를 받는 것을 발견했다. 이러한 형태라면 파라미터를 rvalue reference밖에 받지 못할텐데 ... lvalue는 어떻게 받으려고? 하는 궁금증이 나는 문득 생긴 것이었다.


굳이 copy를 해야 하기 때문에 약간 비효율적인 방식이지만, 왠지 요즘 컴파일러들이라면 이러한 경우 lvalue --> copy constructor(rvalue) --> move semantics를 통하여 컴파일이 될 거라는 생각이 들었다. 과연 정말로 그럴까? 그래서 테스트를 해보기로 하였다!


예를 들어, 이런 코드가 있다고 가정할 때...


#include <iostream>


class prove

{

public:

prove(int x) { this->x = x; std::cout << "new" << std::endl; }

prove(prove &p) { x = p.x; std::cout << "copy" << std::endl; }

prove(prove&& p) { x = p.x; std::cout << "move" << std::endl;  }

private:

int x;

};


template<class T>

void func_temp(T&& t)

{

std::cout << "template passing" << std::endl;

}


void func_rr(prove&& p)

{

std::cout << "rvalue-ref passing" << std::endl;

}


int main()

{

prove p(10);


// 1

std::cout << "1" << std::endl;

func_temp(p);

// 2

//std::cout << "2" << std::endl;

//func_rr(p);

// 3

std::cout << "3" << std::endl;

func_rr(prove(p));


return 0;

}



class prove에 정의한 생성자들로 하여금 어떤 생성자가 호출되는지 확인할 수 있도록 메시지를 프린트하도록 하였다.


...


프로그램의 결과는 다음과 같이 나온다.


new

1

template passing

3

copy

rvalue-ref passing


1번은 template으로 lvalue 인자를 넘기는 것인데, 여기서 재미있는 것은 template 표현상으로는 rvalue reference이지만 실제로는 T가 prove& 타입으로 받아지기 때문에, prove& && --> prove&의 형태로 reduce되어 lvalue로 변한다는 것이다. 어쩐지.. STL 라이브러리들의 인자들이 죄다 이 형태로만 파라미터를 받는 이유가 있었다. lvalue는 어떻게 처리하는지 항상 궁금했었는데. 이를 reference collapsing rule이라고 칭하는 듯. http://thbecker.net/articles/rvalue_references/section_08.html


2번은 syntax상 rvalue reference에 lvalue를 넘기는 것을 허용하지 않으므로 에러가 뜬다. 사실 이 경우 자동으로 [3]번 같이 내부적으로 변환이 일어나지 않을까 기대했는데 ... 그렇지 않았다.


3번은 object copy를 한 후에, move semantics가 호출되기를 의도하였다. syntax 에러가 뜨지 않았기 때문에 컴파일 가능한 형태이다.

헌데, 결과가 예상이랑 약간 상이하였다. move semantics가 호출되지 않는 것이었다.


... 스택오버플로우에 답이 있었다. copy elision 최적화 기법은 /Od 옵션에서도 자동으로 적용된다는 걸 첨 알았다. 그정도로 하찮은 뻘짓거리였던 건가 보다. https://stackoverflow.com/questions/13099603/c11-move-constructor-not-called-default-constructor-preferred


물론, [1]번의 경우 정말로 rvalue reference로만 type을 허용하기를 의도할 수도 있을 것이다. 그러한 경우에 대해서도 역시 스택오버플로우에 답이 있었다. https://stackoverflow.com/questions/7863603/how-to-make-template-rvalue-reference-parameter-only-bind-to-rvalue-reference 킹 갓 빛 버 플 로 우 ...



-- 실험 끝 --