bugfree 님이 쓰신 글 :
: 먼저 답변해주신 박우성님과 빌더님께 감사드립니다
:
: std::map <std::string, int> map;
:
: map에 무작위로 데이타를 넣어두고
:
:
: 1번 방법
:
: for ( const std::pair <std::string, int> & _pair : map)
: std::cout << _pair.second << std::endl;
:
: 2번 방법
:
: for ( const auto & _pair : map )
: std::cout << _pair.second << std::endl;
:
: 두가지 방법으로 출력하는 코드에서 auto의 타입이
: <const std::string, int>라는 빌더님의 설명에 좀 놀랐습니다
: auto가 단순히 타이핑 노가다만 줄여주는거로 알았는데
: 그게 아니었네요
:
: 제가 의문이 더 커지게 된건 박우성님 답변내용 때문인데요
:
: <const std::string, int>에서 const가 빠지는 바람에
: string 값의 복사가 일어나서 그 만큼 시간이 더 걸린다고 하셨습니다
:
: 근데 제가 알고있기론, 그리고 테스트 해본 결과에 따르면
:
: void func (const std::string str) 과
: void func (std::string str) 은
:
: const 있고 없고 상관없이 두 경우 다 복사가 일어납니다
:
: class C {}; 가 있을 때
:
: func (const C obj) 와 func (C obj) 도 호출해보면 마찬가집니다
:
: const는 값을 변경하지 못하게 금지할뿐, 포인터가 아니라
: 두 경우 다 복사가 일어나서 다를게 없잖아요
:
: auto를 사용한 속도가 더 빠른 이유에 의문점이 더 커졌습니다
:
박성우님의 답변내용을 지금 다시 읽어보니...
const가 빠진 결과 string 객체의 복사가 일어나서 그 만큼 시간이 더 걸린다는 문맥으로 읽혀지긴 하네요.
속도가 차이나는 것은... const가 빠져서 string 객체의 복사가 일어났기 때문이 아닙니다.
<const std::string, int> 와 <std::string, int>는...
레퍼런스 또는 포인터에 의한 참조가 아닌.. 값에 의한 참조이기 때문에 const가 있든 없든...
말씀하신 대로.. 두 경우 모두 복사는 일어 납니다.
따라서 두 경우 모두 복사가 일어나는 같은 조건이기 때문에 이 자체가 속도 차이를 나게 하는 요인은 아닙니다.
1번 방법(auto를 사용하지 않은)이 더 느린 이유는... 컴파일러가 Temporary 객체를 생성하기 때문이라고 먼저 답변에서
말씀드렸었는데... 다시 풀어서 얘기를 해보죠.
auto를 사용한 문장을 살펴 봅시다.
for ( const auto &_pair : map )
std::cout << _pair.second << std::endl;
위와 같이 range-based for 문장에서 _pair가 Value 타입이 아닌, Reference 타입으로 지정되어 있기 때문에 컨테이너 map의
Element인 pair에 대한 값의 복사가 일어날 필요가 없습니다. 그래서 컴파일러는 단순히 Iterator를 이용해서 레퍼런스 타입으로
_pair를 참조하는 코드를 생성하면 그만 입니다.
그러나...
auto를 사용하지 않은 1번 방법은...
for ( const std::pair <std::string, int> &_pair : map )
std::cout << _pair.second << std::endl;
컨테이너에서 정의 되어있는 Element의 실제타입 <const Key, Value>가 아닌, 사용자에 의해서 <Key, Value> 타입으로 요구된
경우기 때문에... _pair가 Reference 타입으로 지정되어 있음에도 불구하고, 컴파일러가 타입변환을 위한 Temporary 객체 생성을
시도하게 됩니다.
여기서 Temporary 객체는 타입매칭을 위해 컴파일러에 의해서 생성되는 valu type의 pair<Key, Value> 이고 Reference 타입인
_pair에 바인딩 하게 됩니다. Temporaray 객체가 의도치 않게 생성되는 바람에 Element인 pair에 결합 되어있는 string 객체도
덩달아 불필요하게 생성되는 사이드 이펙트가 발생하게 됩니다.
결론...
const가 빌미를 제공한 건 맞지만...
여기서 속도 차이가 일어나는 근본적인 원인은... const 가 없어서 string 객체의 복사가 일어났기 때문이 아니라...
range-based for 루프 문장에서 타입매칭을 위해 Temporary 객체가 반복적으로 생성/소멸되기 때문 입니다.
컴파일러에 의해서 Temporary 객체가 생성되지 않도록 몇가지 테크닉을 이용할 수도 있지만 실제 코딩을 하다 보면
Temporary 객채의 생성을 피할 수 없는 경우도 있기 때문에... 이런 경우에 템플릿이나 클래스를 구현할 땐... 컴파일러가
C++11을 지원한다면 copy constructor 호출로 인한 복제비용을 줄이기 위해 r-value reference를 이용한 move constructor도
함께 구현해 주는 게 효과적 입니다.
|
제가 갖고있던 의문을 명쾌하게 풀어 주셔서
빌더님 너무 감사합니다