TIL – C++ 폴드 표현식과 쉼표 연산자
C++17에 추가한 폴드 표현식(fold expression)은 매개변수 꾸러미를 연산자에 축약(reduce)하는 방법이다. 단항과 이항, 왼쪽과 오른쪽, 모두 네 가지 방식이 있다. 자세한 내용은 검색해 보면 많이 찾을 수 있고, 여기에서도 볼 수 있다.
단항 기준으로 왼쪽과 오른쪽 접기는 다음처럼 확장한다.
1 2 3 |
auto f1{((100 % 7) % 3)}; auto f2{(100 % (7 % 3))}; |
템플릿으로 작성하면 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 |
template <typename... Ts> auto left_fold_mod(Ts... ts) { return (... % ts); } template <typename... Ts> auto right_fold_mod(Ts... ts) { return (ts % ...); } |
결과는 왼쪽 접기일 때 2
, 오른쪽 접기일 때 0
이다.
그런데 쉼표 연산자와 함께 사용할 때는 어떻게 될까. 왼쪽과 오른쪽 접기를 직접 확장하면 다음과 같다.
1 2 3 4 5 |
std::vector<int> v1; ((v1.push_back(1), v1.push_back(2)), v1.push_back(3)); std::vector<int> v2; (v2.push_back(1), (v2.push_back(2), v2.push_back(3))); |
템플릿은 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 |
template <typename T, typename... Ts> void left_fold_push_back(std::vector<T>& v, Ts... ts) { (..., v.push_back(ts)); } template <typename T, typename... Ts> void right_fold_push_back(std::vector<T>& v, Ts... ts) { (v.push_back(ts), ...); } |
결과는 왼쪽과 오른쪽 접기 모두 {1, 2, 3}
이다. 그런데 괄호 우선 순위가 가장 높지 않던가? 그럼 오른쪽 접기는 {2, 3, 1}
이 아닌가? 혹시 매개변수 꾸러미가 아니라 표현식을 확장할 때는 처리하는 방식이 다를까 생각할 수도 있겠지만 폴드 표현식은 표현식을 포함해 확장한다. 다음을 보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
template <typename T> auto dummy(T val) { return val; } template <typename... Ts> auto left_fold_mod2(Ts... ts) { return (... % dummy(ts)); } template <typename... Ts> auto right_fold_mod2(Ts... ts) { return (dummy(ts) % ...); } |
앞서 본 것과 같은 나머지 연산이지만 더미 함수를 포함해 이제는 표현식을 확장한다. 물론 결과는 이전과 같으며, 표현식을 쓰더라도 확장 방식이 달라지지 않는다는 걸 알 수 있다.
다시 쉼표 연산자를 생각해 보자. 쉼표 연산자는 E1, E2
일 때 E1
연산 후 E2
를 연산한다. 즉 괄호에 상관 없이 이 순서를 따른다. 이는 논리 비교 연산자도 마찬가지이다. 다만 논리 비교 연산자는 단축 평가(short-circuit evaluation)도 하므로 주의한다.
예로 사용한 전체 코드는 다음에서 볼 수 있다.