Roman Perepelitsa (roman.perepelitsa@gmail.com) 작성
최초 게시일: 2016년 1월 7일
최종 업데이트: 2020년 8월 19일
빠른 링크: abseil.io/tips/108
std::bind
를 피하세요
이 팁에서는 코드 작성 시 std::bind()
를 피해야 하는 이유를 요약합니다.
왜 std::bind
를 피해야 하나요?
std::bind()
를 올바르게 사용하는 것은 어렵습니다. 몇 가지 예를 살펴보겠습니다. 아래 코드가 좋아 보이나요?
void DoStuffAsync(std::function<void(absl::Status)> cb);
class MyClass {
void Start() {
DoStuffAsync(std::bind(&MyClass::OnDone, this));
}
void OnDone(absl::Status status);
};
많은 숙련된 C++ 개발자들이 위와 같은 코드를 작성하지만 컴파일되지 않는다는 사실을 발견하곤 합니다. std::function<void()>
와 함께 사용할 때는 동작하지만, MyClass::OnDone
에 추가 매개변수를 넣으면 오류가 발생합니다. 왜 그럴까요?
문제: std::bind()
는 단순히 첫 N개의 인수를 바인딩하지 않습니다.
std::bind()
는 많은 개발자들이 기대하는 것처럼 단순히 첫 N개의 인수를 바인딩하지 않습니다(이 동작은 부분 함수 적용이라고 합니다). 대신 모든 인수를 명시적으로 지정해야 합니다. 올바른 std::bind()
사용법은 다음과 같습니다:
std::bind(&MyClass::OnDone, this, std::placeholders::_1)
너무 번거롭죠? 더 나은 방법이 있을까요? 네, 있습니다. 바로 std::bind_front()
를 사용하는 것입니다. (C++20을 사용할 수 없는 경우, absl::bind_front
를 사용할 수 있습니다.)
std::bind_front(&MyClass::OnDone, this)
부분 함수 적용: std::bind()
는 하지 않지만, std::bind_front()
는 합니다.
std::bind_front()
는 첫 N개의 인수를 바인딩하고 나머지를 완벽하게 전달합니다. 예를 들어, std::bind_front(F, a, b)(x, y)
는 F(a, b, x, y)
로 평가됩니다.
또 다른 문제
다음 코드를 봅시다:
void DoStuffAsync(std::function<void(absl::Status)> cb);
class MyClass {
void Start() {
DoStuffAsync(std::bind(&MyClass::OnDone, this));
}
void OnDone(); // 여기에 absl::Status가 없습니다.
};
OnDone()
은 매개변수를 받지 않지만 DoStuffAsync()
콜백은 absl::Status
를 필요로 합니다. 컴파일 오류가 날 것 같지만, 이 코드는 경고 없이 컴파일됩니다. 이는 std::bind()
가 과도하게 문제를 해결하려고 하기 때문입니다.
이 코드에서는 DoStuffAsync()
에서 발생한 잠재적 오류가 조용히 무시됩니다. 이는 IO 작업이 성공한 것처럼 보이게 만들 수 있으며, 이는 심각한 문제를 초래할 수 있습니다.
std::bind
의 다른 문제
-
std::bind()
는 컴파일 타임 검사를 무효화합니다.
일반적으로 컴파일러는 호출자가 예상보다 많은 인수를 전달하면 알려줍니다. 하지만std::bind()
에서는 그렇지 않습니다. -
std::bind()
는 이동 전용 객체를 올바르게 처리하지 못합니다.
다음 코드는 컴파일되지 않습니다:void Process(std::unique_ptr<Request> req); void ProcessAsync(std::unique_ptr<Request> req) { thread::DefaultQueue()->Add( ToCallback(std::bind(&MyClass::Process, this, std::move(req)))); }
대신
std::bind_front()
를 사용하여 문제를 해결할 수 있습니다. -
std::bind()
는 중첩 호출에서 예상치 못한 동작을 합니다.
중첩된std::bind()
호출은 의도와 다르게 동작할 수 있습니다.std::bind()
대신 람다나 명명된 함수를 사용하는 것이 더 좋습니다.
std::bind
대신 무엇을 사용해야 하나요?
-
람다(lambda):
std::bind()
를 람다로 대체하면 가독성과 안전성이 향상됩니다.std::bind(&MyClass::OnDone, this)
를 다음과 같이 바꿀 수 있습니다:
[this]() { OnDone(); }
-
std::bind_front
:
부분 함수 적용에는std::bind_front
를 사용하는 것이 좋습니다.std::bind(&MyClass::OnDone, this, std::placeholders::_1)
를 다음과 같이 바꿀 수 있습니다:
std::bind_front(&MyClass::OnDone, this)
결론
std::bind()
를 피하세요. 대신 람다나 std::bind_front
를 사용하세요.
추가 자료
- Effective Modern C++, Item 34: Prefer lambdas to
std::bind
이 번역이 도움이 되길 바랍니다! 😊