Function Object (함수 객체)

개념

  • () 연산자를 재정의하여 호출 가능한 객체 (Functor)
  • 일반 함수처럼 사용 가능하지만 상태(state)를 가질 수 있음.

장점

  • 상태를 가질 수 있음 (예: 내부 변수 유지)
  • 인라인 치환성: 특정 상황에서 함수 객체가 함수보다 빠름 (컴파일 시 인라인화 가능)
  • 각 함수 객체는 고유 타입을 가짐.

구현 시 유의사항

  • const 표시: const 인자로도 전달될 수 있도록
  • constexpr 표시: 인자를 컴파일 타임에 파악 가능하다면 컴파일 타임에 계산 가능하게
  • [[nodiscard]] (C++17): 반환값 무시 시 경고 발생
  • 템플릿으로 구현하는 경우가 많다

예시 (상태를 가지는 함수 객체)

  class F {
    int k;
public:
    F(int v) : k(v) {}
    bool operator()(int n) const { return n % k == 0; }  // k의 배수인지 확인
};

// 사용
std::find_if(v.begin(), v.end(), F(3));
  

Closure (클로저)

  • 함수 객체가 스코프 내 변수를 바인딩할 수 있는 일급 함수 객체.
  • Lambda Expression으로 생성됨.

Inline 치환성

개념

  • 함수 호출 시, 실제 함수 호출을 하지 않고 기계어 코드로 치환하는 것.

상황

  • sort 구현 시, 비교 정책을 인자로 받고싶은데 그러면 직접 비교하는 것보다 비용이 증가
  • inline으로 받을 수도 없는게 함수 포인터에 주소를 담는 순간 치환되지않으므로
  • 그러나 Less로 명시적으로 적어주면 저 자리에 들어갈 수 있는 것이 하나밖에 없기에 인라인 치환 가능

예시

  struct Less
{
    inline bool operator()(int a, int b) const { return a > b; }
}

void sort(int* x, int sz, bool(*cmp)(int, int))
void sort(int* x, int sz, Less)
{
    a < b <-> cmp(a, b)
}
  

함수 포인터 vs 함수 객체

  • 함수 포인터: 주소를 저장 → 인라인 치환 안됨.
  • 함수 객체: 템플릿 인자로 전달 → 인라인 치환 가능.

정리

  • 함수 인자를 템플릿으로 구성
    • 장점: 비교 정책이 인라인 치환 가능, sort함수가 하나하나 생성되니까
    • 단점: sort가 많아져서 코드 메모리 증가
  • 온라인 컴파일러(godbolt.org)
    • 보통 인라인 치환 적용 안해서 보여줌
    • x64 msvc v19.lateset, /Ob1 옵션 → 인라인 치환적용하여 확인가능
    • 함수 객체 버전은 다 치환돼서 call이 명령어가 없는 걸 확인가능
    • 함수 포인터 버전은 call 존재
  • cpp이 제공해주는 함수 객체
    • functional 헤더

ADL (Argument Dependent Lookup)과 함수 객체

  • C++20의 constrained algorithm
    • C++98의 알고리즘에 여러 기능 추가 한 것
    • std::ranges namespace 안에 제공
  • 이유: ADL로 인한 이름 충돌 방지.

ADL

  • 개념
    • 함수를 찾을 때 인자가 속해 있는 이름 공간은 자동으로 검색한다는 문법
  • 필요 이유
    • 연산자 재정의 문법에 필요, 편리성
  namespace Graphics
{
    struct Point
    {
        int x, y;
    };
    Point operater+(const Point& p1,
                    const Point& p2)
    {
        return point{p1.x + p2.x, p1.y + p2.y};   
    }
};

int main()
{
    Graphics::Point p1{1, 1};
    Graphics::Point p2{2, 2};

    auto ret2 = p1 + p2; // ADL로 가능
}