목차

  • [[#고차 함수(Higher-order function)|고차 함수(Higher-order function)]]
  • [[#함수 포인터|함수 포인터]]
  • [[#라이브러리 핸들(dll 함수 호출)|라이브러리 핸들(dll 함수 호출)]]
  • [[#투명 연산자 펑터|투명 연산자 펑터]]
  • [[#::bind()|::bind()]]
  • [[#람다|람다]]
  • [[#std::Invoke|std::Invoke]]
  • [[#트레일링 리턴타입을 쓰는 이유|트레일링 리턴타입을 쓰는 이유]]
    • [[#트레일링 리턴타입을 쓰는 이유#✅ 1. 리턴 타입을 명확히 하고 싶을 때|✅ 1. 리턴 타입을 명확히 하고 싶을 때]]
    • [[#트레일링 리턴타입을 쓰는 이유#✅ 2. 리턴 타입이 **복잡하거나 추론 불가능할 때|✅ 2. 리턴 타입이 **복잡하거나 추론 불가능할 때]]
    • [[#트레일링 리턴타입을 쓰는 이유#✅ 3. 리턴 타입이 auto인 경우라도 const ref 등 복잡한 타입이면|✅ 3. 리턴 타입이 auto인 경우라도 const ref 등 복잡한 타입이면]]
    • [[#트레일링 리턴타입을 쓰는 이유#✅ 정리|✅ 정리]]

고차 함수(Higher-order function)

  • 다른 함수들을 인자로 받거나, 결과값으로 반환하는 함수

    • 함수 포인터, function
  • 현대에서 함수 포인터보다 function 이 많이 사용됨 (편리성, 가독성, 타입 안정성 등)

  • 특징

    • 함수를 인자로 받을 수 있음

        #include <iostream>
        #include <functional>
      
        void applyFunction(std::function<int(int)> func, int value) {
            std::cout << "Result: " << func(value) << std::endl;
        }
      
        int square(int x) {
            return x * x;
        }
      
        int main() {
            applyFunction(square, 5); // square 함수를 전달
            return 0;
        }
    • 함수를 반환할 수 있음

        #include <iostream>
        #include <functional>
      
        std::function<int(int)> multiplier(int factor) {
            return [factor](int x) { return x * factor; };
        }
      
        int main() {
            auto timesTwo = multiplier(2);
            std::cout << "2 x 5 = " << timesTwo(5) << std::endl; // 출력: 2 x 5 = 10
            return 0;
        }

함수 포인터

  • 함수의 주소를 저장하고 참조하는 데 사용되는 포인터

  • 멤버 함수를 전달하기 위해서는 std::bind 를 통해서 객체와 결합된 Wrapper 필요

  • 사용 예시

     #include <iostream>
     using namespace std;
    
     // 함수 정의
     int add(int a, int b) { return a + b; }
    
     int main() {
         // 함수 포인터 선언
         int (*funcPtr)(int, int) = add;
    
         // 함수 포인터 호출
         cout << "Result: " << funcPtr(5, 3) << endl; // 출력: Result: 8
         return 0;
     }
  • 함수 테이블로 사용 예시

      int add(int a, int b) { return a + b; }
      int subtract(int a, int b) { return a - b; }
    
      int main() {
          // 함수 포인터 배열
          int (*operations[2])(int, int) = {add, subtract};
    
          cout << "Add: " << operations[0](5, 3) << endl; // 출력: Add: 8
          cout << "Subtract: " << operations[1](5, 3) << endl; // 출력: Subtract: 2
          return 0;
      }
  • Wrapper 이용 멤버 함수 전달

      #include <iostream>
      #include <functional>
    
      class MyClass {
      public:
          void memberCallback(int value) {
              std::cout << "Member function called with value: " << value << std::endl;
          }
      };
    
      void executeCallback(const std::function<void(int)>& callback, int arg) {
          callback(arg);
      }
    
      int main() {
          MyClass obj;
    
          // std::function을 사용하여 멤버 함수 래핑
          std::function<void(int)> callback = std::bind(&MyClass::memberCallback, &obj);
    
          // 콜백 함수 실행
          executeCallback(callback, 42);
    
          return 0;
      }

라이브러리 핸들(dll 함수 호출)

  • HMODULE lib { ::LoadLibrary("hardware.dll") };
    • 이렇게 호출해서 리턴된 결과를 라이브러리 핸들이라 함
    • 호출 과정에서 error발생 시, NULL 리턴
  • 타입 앨리어스를 이용해서 프로토타입을 가진 함수에 대한 포인터 이름 정의 가능
    • using ConnectFunction = int(__stdcall*)(bool, int, const char*);
  • DLL에 있는 함수 포인터 구하기
    • ConnectFunction connect { (ConnectFunction)::GetProcAddress(lib, "Connect");}

투명 연산자 펑터

  • 비투명 연산자 펑터보다 성능이 우수 (가능하면 투명 연산자 펑터 사용)
    • 타입을 추론하여 읽기 떄문에, 형변환이 발생x
  • multiplies<int> -> multiplies<> 로 적어도 됨
  • 이종 룩업 : C14에 도입;
    • 연관 컨테이너에서 키 타입과 다른 타입을 사용하여 검색 수행할 수 있도록 지원
set<string, less<>> mySet;
auto i1 { mySet.find("Key") };     // 스트링 생기지도 않고 메모리 할당x
auto i2 { mySet.find("Key"sv) };   // 스트링 생기지도 않고 메모리 할당x

::bind()

  • <functional> 의 함수
  • 함수 포인터가 멤버 변수를 전달할 떄, 객체와 wrapping 하기위해 많이 사용
  • bind() 의 매개변수는 기본적으로 복제본을 생성
    • bind(func, ref(index)) 를 하면 참조로 넘김

람다

  • 람다 선언자(lambda introducer), 캡처 블록 : []

    • 변수를 캡처해서 람다 표현식 본문에 쓸 떄는, 해당 대괄호 안에 넣기
  • 기본 사용 예제

      auto basicLambda { []{ cout << "Hello from Lambda" << endl; } };
      basicLambda();
  • 람다 클로저 : 컴파일러는 모든 람다 표현식을 자동으로 함수 객체로 변환

  • 트레일링 리턴타입 : ->

  • 람다의 () 오퍼레이터 호출은 기본적으로 const, 따라서 멤버변수(캡쳐변수)의 값 변경이 불가

    • mutable 변수를 캡처변수로 사용 시에는 변경 가능
    • 캡처변수에 &를 붙이면 레퍼런스로 캡처 시, 변경 가능
  • 캡처 디폴트 : 람다 표현식이 속한 상위 스코프의 변수를 모두 캡처하는 방법

    • [=] : 스코프에 있는 변수를 모두 값으로 캡처
    • [&] : 스코프에 있는 변수를 모두 레퍼런스로 캡처
      • [=, &x, &y] , [&, x] 등 여러 조합으로 가능
      • [*this] : 현재 객체의 복제본을 캡처 (실행 시점에 객체가 살아 있지 않을 떄, 유용)
    • [p = move(myPtr)]{} 와 같이 std::move 를 비롯한 모든 종류의 표현식으로 초기화 가능
  • 람다 표현식을 함수의 return 타입으로 반환 가능

    • C14 에서는 auto 함수 리턴 추론을 통해 쉽게 사용가능

std::Invoke

  • 모든 콜러블 객체에 대해 일련의 매개변수를 지정해서 호출 가능
  • invoke({콜러블객체}, {매개변수})

트레일링 리턴타입을 쓰는 이유

✅ 1. 리턴 타입을 명확히 하고 싶을 때

컴파일러가 리턴 타입을 제대로 추론하지 못하거나, 오버로드, 템플릿 등의 상황에서 헷갈릴 수 있을 때 명시적으로 써주는 게 좋아요.

auto func = [](bool b) -> int {
    if (b) return 1;
    else return 2;
};

이 경우는 사실 없어도 되지만, 복잡한 상황에선 명시가 도움이 됩니다.


✅ 2. 리턴 타입이 **복잡하거나 추론 불가능할 때

특히 auto 키워드가 리턴 타입에 들어가는 경우나, 템플릿 코드에서는 반드시 써야 할 때도 있어요.

예:

template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

여기선 a + b의 타입을 컴파일러가 추론하도록 하면서도, 템플릿 밖에서 리턴 타입을 지정해야 하기 때문에 -> decltype(...)이 필요합니다.

람다에서도 똑같아요:

auto l = [](auto a, auto b) -> decltype(a + b) {
    return a + b;
};

✅ 3. 리턴 타입이 auto인 경우라도 const ref 등 복잡한 타입이면

예:

auto l = [](const std::string& s) -> const std::string& {
    return s;
};

이런 식으로 참조 타입, const, 포인터 등을 정확히 명시할 때 ->는 필요합니다.


✅ 정리

상황 -> 필요 여부
간단한 리턴 (int, double 등) ❌ 없어도 됨
리턴 타입 추론 어려움 ✅ 필요함
템플릿, auto, decltype 등 복잡한 타입 ✅ 필요함
const, 참조, 포인터 리턴 등 ✅ 필요함

'공부 > C++' 카테고리의 다른 글

STL활용_vector로 set 대체  (0) 2025.04.29
union  (0) 2025.04.10
(cpp17)string_view  (2) 2025.04.09
예외처리(simple)  (0) 2025.04.09
Attribute  (0) 2025.03.27

+ Recent posts

let textNodes = document.querySelectorAll("div.tt_article_useless_p_margin.contents_style > *:not(figure):not(pre)"); textNodes.forEach(function(a) { a.innerHTML = a.innerHTML.replace(/`(.*?)`/g, '$1'); });