C++11에서부터 사용 가능한 for loop에서 auto와 ranged loop를 사용할 때의 주의점.
auto를 사용한 ranged loop는 크게 다음과 같이 4가지 방식을 생각해 볼 수 있다.
for (auto i : list)
for (auto& i : list)
for (const auto i : list)
for (const auto& i : list)
뒤에 &가 붙은 경우를 먼저 생각해 보면, &가 붙으면 해당 값을 복사하지 않고 참조한다는 의미이다.
아래 코드에서 auto i를 사용하면 list에서 i를 새로 만들어서 값을 복사해서 넣었으므로 i = 0을 수행해도 원본 list의 값은 바뀌지 않는다. 반면 auto& i를 사용하면 원본 list의 값이 바뀌게 된다.
for (auto i : list)
i = 0;
for (auto& i : list)
i = 0;
const가 붙으면 해당 값을 변경하지 못하게 read only로 사용한다는 의미이고, 빌드할 때 값을 대입하려고 하면 오류가 발생한다. 즉 아래와 같이 i = 0을 입력하면 아예 빌드가 되지 않는다.
for (const auto i : list)
i = 0;
for (const auto& i : list)
i = 0;
이를 정리해 보면, 원본 list의 값을 바꾸기 위해서는 auto& 만을 사용해야 하고 원본 list의 값을 읽기만 하는 경우에는 어떤 것을 사용해도 된다. 그 중에서 어떤 것을 사용하는 것이 좋을까?
auto& 대신 auto를 사용하면 값을 복사하면서 새 변수를 생성하는 것과 같다. 즉 단순히 읽기만 하는데 불필요하게 값을 복사할 필요는 없으므로 auto&를 쓰는 게 성능상 좋다.
하지만 auto&를 쓰면 실수로 원본 list의 값을 바꾸는 일이 발생할 수 있다. 따라서 const를 붙여서 const auto&를 사용하면 실행 성능도 좋아지고 안전하고 이해하기 쉬운 코드가 된다.
나 같은 경우에는 항상 const auto&로 코딩을 하고, 원본 값을 바꿔 주어야 하는 경우에만 auto&를 사용하고 있다.
드물게 변수 복사가 필요해서 auto가 필요한 경우도 있긴 하지만 많지는 않은 것 같고, 이럴 때는 굳이 auto를 쓰지 않기도 한다.
아마도... 클래스가 아닌 int 같은 기본 자료형은 굳이 auto&를 사용하지 않아도 auto를 써도 컴파일러가 알아서 효율적으로 처리한다고 알고 있다. 그래서 이름이 길고 복잡한 클래스에 대해서는 auto를 자주 쓰지만, 기본 자료형은 아예 auto를 쓰지 않는 걸 선호한다.
유의점은 vector 같은 경우 vector 내부적으로 최적화를 위해 1 byte가 아니라 1 bit를 사용하기 때문에 auto&를 사용할 수 없어서 다음과 같이 사용해야 한다고 한다. (참조: https://ozt88.tistory.com/3, https://stackoverflow.com/questions/34079390/range-for-loops-and-stdvectorbool)
for (auto i: boolVector) // Read-only
for (auto&& i: boolVector) // Read/write