728x90
반응형
728x90
반응형
반응형

C++23에서 std::function::target_type::operator==가 제거된 이유

1. 개요

  • C++ 표준 라이브러리의 std::function타입 소거(type erasure) 기법을 통해 다양한 함수 객체를 저장하고 호출할 수 있는 유연한 래퍼(wrapper) 입니다.
  • 하지만 C++11에서는 std::function::target_type()의 반환값을 통한 operator== 비교가 가능했으며, 이는 설계상 애매하고 잘못 사용될 소지가 있다는 지적을 받아 결국 C++23에서 제거되었습니다.


2. 문제점과 제거 이유

2.1. 애매한 의미

  • std::function::target_type()은 함수 객체의 타입 정보를 std::type_info 형태로 반환하지만, 이 타입 비교는 typeid 비교와 다를 바 없고 코드의 명확성을 떨어뜨렸습니다.

2.2. 타입 소거 설계 취지 훼손

  • std::function은 내부 타입을 감추고 콜러블 호출만 보장하는 추상화 도구입니다.
  • 타입 비교는 타입 소거의 철학에 맞지 않으며 잘못된 설계 방향으로 여겨졌습니다.

2.3. 안정성과 구현 의존성 문제

  • std::type_info::operator== 비교는 컴파일러마다 구현 방식이 다를 수 있으며, 이로 인한 혼동을 방지하기 위해 제거되었습니다.


3. C++11 예제 (C++23 이전에 허용되었던 코드)

  • cpp

      #include <iostream>
      #include <functional>
      #include <typeinfo>
      
      void func() { std::cout << "Hello\n"; }
      
      int main() {
          std::function<void()> f = func;
      
          // C++11에서는 이렇게 비교 가능
          if (f.target_type() == typeid(void(*)())) {
              std::cout << "타겟 타입이 void(*)() 입니다.\n";
          }
      
          return 0;
      }
    

  • output

      타겟 타입이 void(*)() 입니다.
    


4. C++23 예제 코드

  • C++23에서는 operator== 비교가 제거되어 컴파일 에러가 발생합니다.

  • cpp

      #include <iostream>
      #include <functional>
      #include <typeinfo>
      
      void func() { std::cout << "Hello\n"; }
      
      int main() {
          std::function<void()> f = func;
      
          // C++23에서는 컴파일 오류 발생!
          if (f.target_type() == typeid(void(*)())) {
              std::cout << "타겟 타입이 void(*)() 입니다.\n";
          }
      
          return 0;
      }
    

  • ❌ 컴파일 에러 메시지 예시:

      error: use of deleted function 'bool std::type_info::operator==(const std::type_info&) const = delete'
    


5. C++23 이후 권장되는 패턴

  • 타입을 확인하고 싶다면 target<>()를 사용하여 해당 타입으로 변환 가능한지 확인하는 방식이 권장됩니다.

  • cpp

      #include <iostream>
      #include <functional>
      
      void func() { std::cout << "Hello\n"; }
      
      int main() {
          std::function<void()> f = func;
      
          if (f.target<void(*)()>() != nullptr) {
              std::cout << "타겟 타입이 void(*)() 입니다.\n";
          }
      
          return 0;
      }
    

  • 출력:

      타겟 타입이 void(*)() 입니다.
    


6. 요약

  • 결론적으로 std::function::target_type::operator==는 의미의 모호함, 타입 소거 철학 훼손, 구현 의존성을 이유로 C++23에서 삭제되었습니다.

  • 앞으로는 f.target<Foo>() != nullptr 형태로 타입 확인을 하는 것이 올바른 방식입니다.




  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++20에서 std::tr1이 제거된 이유

1. std::tr1의 등장 배경

  • std::tr1Technical Report 1 (TR1) 기능을 임시적으로 표준 라이브러리에 제공하기 위해 만들어진 namespace 입니다.
  • TR1C++03 이후 새롭게 추가될 기능들을 실험적으로 제공 하는 목적이었으며, 향후 표준에 정식 포함되는 것을 목표로 했습니다.


2. std::tr1의 기능과 사용 예제

  • TR1에는 스마트 포인터, 함수 객체 래퍼, 해시 기반 컨테이너 등 유용한 기능이 포함되어 있었습니다.

  • C++03 시절에 다음과 같이 사용되었습니다:

  • cpp

      // TR1 사용 예제 (C++03 + TR1)
      #include <tr1/memory>
      #include <tr1/unordered_map>
      #include <iostream>
      
      int main() {
          // std::tr1::shared_ptr 사용
          std::tr1::shared_ptr<int> p(new int(42));
          std::cout << "shared_ptr 값: " << *p << std::endl;
      
          // std::tr1::unordered_map 사용
          std::tr1::unordered_map<std::string, int> umap;
          umap["apple"] = 5;
          umap["banana"] = 3;
      
          for (auto& pair : umap) {
              std::cout << pair.first << ": " << pair.second << std::endl;
          }
      
          return 0;
      }
    


3. 제거된 이유

  • C++11에서 std::shared_ptr, std::function, std::unordered_map 등 대부분의 기능이 std 네임스페이스로 정식 포함되었습니다.

  • std::tr1 네임스페이스는 더 이상 필요 없는 중복이 되었으며, 혼란과 유지보수 비용을 줄이기 위해 C++20에서 제거되었습니다. 💔



4. 현대적인 대체 코드 (C++11 이후)

  • 같은 기능을 C++11 이후에는 이렇게 사용합니다:

  • cpp

      #include <memory>
      #include <unordered_map>
      #include <iostream>
      
      int main() {
          // std::shared_ptr 사용
          std::shared_ptr<int> p = std::make_shared<int>(42);
          std::cout << "shared_ptr 값: " << *p << std::endl;
      
          // std::unordered_map 사용
          std::unordered_map<std::string, int> umap;
          umap["apple"] = 5;
          umap["banana"] = 3;
      
          for (auto& pair : umap) {
              std::cout << pair.first << ": " << pair.second << std::endl;
          }
      
          return 0;
      }
    


5. 요약

  • std::tr1은 표준화 이전 임시 네임스페이스 였으며,
    • 기능이 정식 표준에 포함된 이후에는 더 이상 쓸 필요가 없어졌습니다.
  • 결국 C++20 표준에서 완전히 제거되어, 현대 C++에서는 std 네임스페이스만 사용하시면 됩니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++20에서 std::result_of가 제거된 이유


1. 제거 사유

  • std::result_ofC++11부터 함수 호출 시 반환 타입을 추론하기 위한 메타프로그래밍 도구로 사용되었습니다.

  • 하지만 다음과 같은 이유로 C++20에서 공식적으로 제거되었습니다:
    • 모호함과 복잡성:
      • std::result_of는 함수 타입 표기법이 복잡했고, 사용 시 항상 decltype(func)& 형태나 함수 포인터, 참조 등으로 작성해야 해서 실수가 잦았습니다.
    • 표현력 부족:
      • std::result_ofinvocable 개념이나 constexpr 함수, 다양한 Callable 타입들을 다루는 데 제약이 있었습니다.
    • 대체제의 등장:
      • C++17에서 std::invokestd::invoke_result가 표준으로 추가되며 훨씬 더 강력하고 일관된 함수 호출 및 타입 추론 메커니즘을 제공했습니다.
      • std::invoke_result는 함수 객체, 멤버 포인터, 람다 등 복잡한 callable 형태도 일관되게 처리할 수 있습니다.
      • std::invoke는 함수 포인터, 멤버 함수 포인터, 일반 함수 객체를 하나의 방식으로 호출 가능합니다.


2. C++11 예제 (std::result_of 사용)

  • cpp

#include <iostream>
#include <type_traits>

int func(double) { return 42; }

int main() {
    // func(double) 호출 결과 타입 추론
    std::result_of<decltype(func)&(double)>::type val = func(3.14);
    std::cout << val << std::endl;  // 출력: 42
}

  • 단점: decltype(func)&(double)처럼 다소 복잡하고 실수하기 쉽습니다.


3. C++20 예제 (std::invoke_result 사용)

  • cpp

      #include <iostream>
      #include <type_traits>
      #include <functional>
      
      int func(double) { return 42; }
      
      int main() {
          // std::invoke_result 사용
          std::invoke_result<decltype(func), double>::type val = func(3.14);
          std::cout << val << std::endl;  // 출력: 42
      
          // 또는 auto로 간단하게 
          auto val2 = func(3.14);
          std::cout << val2 << std::endl;  // 출력: 42
      }
    

  • 장점: std::invoke_result는 표기가 더 간단하고, 멤버 함수 포인터나 다양한 callable 타입에 대해 일관되게 적용됩니다.
  • auto를 사용하는 것이 대부분의 실용적인 상황에서 훨씬 깔끔합니다.


4. 정리

  • 항목 std::result_of (C++11~C++17) std::invoke_result (C++17~)
    복잡성 표기법이 복잡하고 실수하기 쉬움 더 단순하고 직관적임
    지원 범위 함수 포인터, 함수 객체 위주 제한적 지원 함수 객체, 멤버 함수 포인터, 람다 등 폭넓게 지원
    제거 여부 C++20에서 제거됨 C++20 이후 표준
    추천 사용 더 이상 사용 권장 안 함 std::invoke_result 또는 auto 사용 권장



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++20에서 std::allocator<void>가 제거된 이유

1. 제거된 이유

  • std::allocator<void>C++98C++03 표준에서 특수한 형태의 allocator 로 존재했습니다.
  • 이 타입은 void는 객체를 생성할 수 없는 타입이므로 실제 메모리 할당보다는 allocator_traits나 포인터 관련 타입 변환을 지원하기 위한 보조 용도로 존재했습니다.

  • 그러나 C++11 이후 std::allocator_traits가 도입되면서 void 타입도 일반 allocator가 자동으로 처리 가능해졌습니다.
  • 결국 std::allocator<void>는 중복적이고 불필요해졌으며, 표준을 더 깔끔하게 만들기 위해 C++20에서 공식적으로 제거 되었습니다.


2. C++98 예제 (std::allocator<void> 사용)

  • cpp

      #include <memory>
      #include <iostream>
      
      // void* 포인터를 매개변수로 받아 출력하는 함수
      void printPointer(std::allocator<void>::pointer p) {
          std::cout << "전달된 void 포인터 주소: " << p << std::endl;
      }
      
      int main() {
          // void 타입에 대한 allocator 선언 (C++98 방식)
          std::allocator<void> void_alloc;
          // void_alloc은 실제 메모리 할당이 불가능하며 타입 변환 용도로만 존재합니다.
      
          // void* 타입 포인터를 위한 rebind (nullptr 사용)
          std::allocator<void>::pointer p = nullptr;
      
          // 함수에 void* 포인터 전달
          printPointer(p);
      
          std::cout << "C++98: std::allocator<void>는 실제 메모리 할당 없이 포인터 변환 및 타입 정의용으로만 사용됩니다." << std::endl;
      
          return 0;
      }
    


3. C++20 이후 예제 (std::allocator<void> 제거 대체)

  • cpp

      #include <memory>
      #include <iostream>
      
      void printPointer(int* p) {
          std::cout << "전달된 int 포인터 주소: " << p << std::endl;
      }
      
      int main() {
          // void 타입 allocator는 존재하지 않으므로 필요 없습니다.
          // 대신 std::allocator_traits로 처리됩니다.
          
          // int형에 대한 표준 할당자 생성
          std::allocator<int> alloc;  
          
          // allocator_traits를 통해 포인터 타입을 추론하고 메모리 할당
          std::allocator_traits<std::allocator<int>>::pointer p = alloc.allocate(1);
          // allocate(1)은 int 타입 객체를 1개 만들 수 있는 메모리 공간을 할당한다는 의미
      
          // 함수에 int* 포인터 전달
          printPointer(p);
      
          std::cout << "C++20: std::allocator<void> 없이 allocator_traits로 처리합니다." << std::endl;
      
          alloc.deallocate(p, 1);
      
          return 0;
      }
    


4. 정리

  • 버전 std::allocator<void> 필요성
    C++98/03 존재 void용 특수 포인터 타입 지원
    C++11~17 존재 (그러나 권장 안함) allocator_traits로 대부분 대체
    C++20 이후 제거 allocator_traits가 모든 void 타입 처리 지원
  • std::allocator<void>는 이제 역사 속으로 사라졌으며, 현대 C++에서는 std::allocator_traits가 그 역할을 깔끔하게 대체합니다.




  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++20에서 std::iterator가 삭제된 이유


1. 왜 std::iterator가 삭제되었을까?

  • std::iteratorC++98부터 제공되었으며 커스텀 이터레이터 작성 시 trait 정보를 간편하게 지정하기 위해 사용되었습니다.

    • trait 정보는 컴파일 타임에 타입과 관련된 정보를 제공하는 메타데이터입니다.
    • 이터레이터의 경우, 표준 라이브러리나 알고리즘이 이터레이터의 성격과 동작 방식을 알아내기 위해 사용하는 정보입니다.
  • 그러나:

    • 불필요한 상속을 강요 하며 코드의 복잡성을 증가시키고,
    • C++11 이후 등장한 usingstd::iterator_traits만으로 충분히 대체 가능해졌습니다.

  • 이러한 이유로 std::iteratorC++17에서 deprecated 처리되었고,
    • C++20에서 최종적으로 삭제되었습니다.


2. C++98 방식 — std::iterator 사용 예제

  • C++98에서는 std::iterator를 상속받아 trait 정보를 한 번에 지정하는 방식을 사용할 수 있었습니다.

  • cpp

      #include <iostream>
      #include <iterator>
      
      // C++98에서도 제공되는 std::iterator 를 상속하여 trait 정의
      class MyIterator : public std::iterator<std::forward_iterator_tag, int>
      {
          int* ptr;
      public:
          MyIterator(int* p) : ptr(p) {}
      
          // 역참조 연산자
          int& operator*() { return *ptr; }
      
          // 전위 증가 연산자
          MyIterator& operator++() { ++ptr; return *this; }
      
          // 비교 연산자
          bool operator!=(const MyIterator& other) const { return ptr != other.ptr; }
      };
      
      int main() {
          int arr[] = {1, 2, 3, 4, 5};
          MyIterator begin(arr);
          MyIterator end(arr + 5);
          
          // C++98에서는 auto 미지원 -> 타입을 직접 명시
          for (MyIterator it = begin; it != end; ++it) {
              std::cout << *it << " ";
          }
          std::cout << std::endl;
          return 0;
      }
    

  • 상속 방식은 간편했지만 코드 가독성과 유지 관리 측면에서 한계가 있었습니다.


3. C++20 방식 — std::iterator 제거 후 권장 예제

C++20에서는 std::iterator 대신 using 키워드를 사용해 trait 정보를 직접 정의하는 것이 권장됩니다.

  • cpp

      #include <iostream>
      #include <iterator>
      
      // C++20 스타일: using 키워드로 trait 직접 정의
      class MyIterator {
          int* ptr;
      public:
          // 이 이터레이터가 forward iterator임을 알려주는 태그
          using iterator_category = std::forward_iterator_tag;
          
          // 반복 시 다루는 값의 타입
          using value_type = int;
          
          // 두 반복자 간의 차이를 나타내는 타입
          using difference_type = std::ptrdiff_t;
          
          // 반복자가 가리키는 포인터 타입
          using pointer = int*;
          
          // 반복자가 가리키는 참조 타입
          using reference = int&;
      
          MyIterator(int* p) : ptr(p) {}
          int& operator*() { return *ptr; }
          MyIterator& operator++() { ++ptr; return *this; }
          bool operator!=(const MyIterator& other) const { return ptr != other.ptr; }
      };
      
      int main() {
          int arr[] = {1, 2, 3, 4, 5};
          MyIterator begin(arr);
          MyIterator end(arr + 5);
          
          // C++20에서는 auto 사용 가능
          for (auto it = begin; it != end; ++it) {
              std::cout << *it << " ";
          }
          std::cout << std::endl;
          return 0;
      }
    

  • using 선언은 이 이터레이터가 표준 알고리즘에서 올바르게 동작할 수 있도록 필수 정보를 전달합니다.


4. 요약

  • std::iteratorC++98부터 존재했지만, 코드 구조상 불필요한 상속과 복잡성을 유발했습니다.
  • C++20에서는 std::iterator를 제거하고 직접 using 선언을 통한 trait 정의 방식을 표준으로 삼았습니다.
  • iterator_category와 같은 trait는 표준 라이브러리 내부 알고리즘(std::advance, std::distance 등)에서 이터레이터의 성격을 판단하는 데 사용됩니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++17에서 std::random_shuffle이 삭제된 이유


1. 삭제 이유

  • 낮은 난수 품질
    • std::random_shuffle은 내부적으로 rand()를 사용할 수 있어 난수 품질이 낮고, 일관된 시드 관리도 어렵습니다.
  • 명시적 난수 엔진 전달 불가
    • 난수 생성 방식을 개발자가 직접 제어하기 어렵습니다.
  • 보다 안전한 대안 등장
    • C++11 이후 std::shuffle이 등장하면서, 개발자가 고품질 난수 엔진 (std::mt19937 등)을 직접 지정 해 줄 수 있게 되어, 재현성과 안정성이 크게 향상되었습니다.
  • 표준화 방향
    • 표준 위원회는 안전하고 예측 가능한 코드 작성을 위해 std::random_shuffleC++14에서 deprecated, C++17에서 완전 삭제, C++20에서는 제거했습니다.


2. C++98 코드 예제 (std::random_shuffle)

  • cpp

      #include <iostream>
      #include <vector>
      #include <algorithm>
      #include <cstdlib>
      #include <ctime>
      
      int main() {
    
          // 랜덤 시드(random seed) 설정
          std::srand(unsigned(std::time(0))); 
          // std::time(0)는 현재 시간이며, 랜덤 시드 값으로 사용됨
          // std::srand()는 단순한 선형 합동 생성기(LCG)를 기반
      
          std::vector<int> v {1, 2, 3, 4, 5};
      
          // std::random_shuffle 사용 (C++98)
          // 주어진 범위의 요소들을 무작위로 섞는 기능을 제공
          std::random_shuffle(v.begin(), v.end());
      
          for (int n : v)
              std::cout << n << " ";
          std::cout << std::endl;
      
          return 0;
      }
    

  • std::random_shuffle은 내부적으로 rand() 기반으로 동작합니다.


3. C++20 코드 예제 (std::shuffle)

  • cpp

      #include <iostream>
      #include <vector>
      #include <algorithm>
      #include <random>
      
      int main() {
          std::vector<int> v {1, 2, 3, 4, 5};
    
          // 난수 생성 장치 (C++11)
          // 하드웨어 기반 난수 발생기(Hardware RNG)가 지원되는 경우 해당 장치를 사용
          // 그렇지 않으면 구현 종속적인 의사난수를 생성하는 장치
          std::random_device rd;
    
          // 메르센 트위스터(Mersenne Twister) 난수 생성기 엔진 (C++11)
          // 19937은 주기(period)가 2^19937 − 1 라는 의미
          std::mt19937 g(rd()); 
      
          // std::shuffle 사용 (C++11)
          // 주어진 범위의 요소들을 무작위로 섞는 기능을 제공
          std::shuffle(v.begin(), v.end(), g);
      
          for (int n : v)
              std::cout << n << " ";
          std::cout << std::endl;
      
          return 0;
      }
    

  • std::shuffle은 명확한 난수 엔진을 전달받아 보다 안전하고 예측 가능한 결과를 제공합니다.


4. 표준 랜덤 엔진

4.1. 랜덤 품질 기준 엔진

  • 엔진 이름 설명 품질 및 특징
    std::minstd_rand0 가장 기본적인 LCG(선형 합동 생성기). 매우 단순하고 주기가 짧음. 매우 낮음
    std::minstd_rand minstd_rand0보다 약간 더 나은 계수를 사용하지만 여전히 LCG 방식. 낮음
    std::ranlux24_base 기본 RANLUX 생성기, 품질은 좋지만 속도가 느림. 중간 이하
    std::ranlux48_base 48비트 버전의 RANLUX 기본 엔진. ranlux24_base보다 더 나음. 중간
    std::mt19937 메르센 트위스터 32비트 버전. 긴 주기와 좋은 품질을 가짐. 중상
    std::mt19937_64 메르센 트위스터 64비트 버전. 더 큰 정밀도와 품질을 제공함. 중상~높음
    std::ranlux24 RANLUX 알고리즘 최적화 버전. 고품질 난수 생성기. 높음
    std::ranlux48 매우 고품질의 RANLUX 난수 생성기. 신뢰성과 품질이 최고 수준. 매우 높음
    std::random_device 하드웨어 엔트로피 기반 난수. 예측 불가능하며 품질이 가장 높음 (단, 느리거나 시스템 의존적일 수 있음). 최고 수준

4.2. 랜덤 속도 기준 엔진

  • 엔진 이름 설명 속도 및 특성
    std::minstd_rand0 매우 단순한 LCG, 가장 빠르지만 품질은 낮음. 매우 빠름
    std::minstd_rand 단순 LCG, 빠르고 약간 더 안정적임. 매우 빠름
    std::mt19937 메르센 트위스터 32비트 버전, 상당히 빠르고 고품질. 빠름
    std::mt19937_64 64비트 메르센 트위스터, 속도는 약간 느리지만 여전히 충분히 빠름. 빠름~보통
    std::ranlux24_base RANLUX의 기본형, 상대적으로 느림. 느림
    std::ranlux48_base 더 높은 정밀도의 RANLUX 기본형, 더 느림. 느림
    std::ranlux24 고품질 RANLUX, 매우 느리지만 안정적임. 매우 느림
    std::ranlux48 RANLUX 고급형, 최고 품질이지만 속도가 가장 느림. 매우 느림
    std::random_device 하드웨어 또는 OS 엔트로피 기반, 매우 느리고 블로킹 가능성도 있음. 매우 느림 (불규칙)



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++17에서 std::unary_functionstd::binary_function 가 제거된 이유

1. 개요

  • std::unary_functionstd::binary_functionC++98 표준에서 함수 객체를 만들 때 인자 및 반환 타입 을 명시적으로 제공하기 위해 사용되던 구조체입니다.
  • 그러나 C++17 표준부터는 이 두 기능이 제거되었습니다.
  • 이는 더 이상 필요하지 않기 때문이며, 현대 C++auto, decltype, std::function, 람다 등을 통해 타입 추론과 함수 객체 작성이 더 직관적이고 강력해졌습니다.


2. 제거 이유 요약

  • 불필요한 중복 : 현대 C++은 타입 추론 기능이 강력해 굳이 typedef 제공이 필요 없음
  • 표준 라이브러리 리팩토링 : std::plus, std::less 등도 해당 기반을 사용하지 않고 구현
  • 메타 프로그래밍 개선 : decltypestd::invoke 등이 등장하면서 복잡함이 사라짐


3. C++98 예제

  • cpp

      #include <iostream>
      #include <functional>
      
      // std::unary_function 사용 예제
      // std::unary_function 는 함수 인자 1개만 사용 가능
      struct PrintInt : public std::unary_function<int, void> { // 인자 (int), 반환값 void
          void operator()(int x) const {
              std::cout << "값: " << x << std::endl;
          }
      };
      
      // std::binary_function 사용 예제  
      // std::binary_function는 함수 인자 2개만 사용 가능
      struct Add : public std::binary_function<int, int, int> { // 인자 (int, int), 반환값 void
          int operator()(int a, int b) const {
              return a + b;
          }
      };
      
      int main() {
          PrintInt print;
          print(10);
      
          Add add;
          std::cout << "합: " << add(3, 4) << std::endl;
      
          return 0;
      }
    


4. C++17 이후 예제

  • cpp

      #include <iostream>
      #include <functional>
      
      // std::unary_function, std::binary_function 없이 작성
      struct PrintInt {
          void operator()(int x) const {
              std::cout << "값: " << x << std::endl;
          }
      };
      
      struct Add {
          int operator()(int a, int b) const {
              return a + b;
          }
      };
      
      int main() {
          PrintInt print;
          print(10);
      
          Add add;
          std::cout << "합: " << add(3, 4) << std::endl;
      
          // 또는 람다 함수 사용
          auto add_lambda = [](int a, int b) { return a + b; };
          std::cout << "람다 합: " << add_lambda(5, 7) << std::endl;
      
          return 0;
      }
    



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++17에서 std::mem_funstd::mem_fun_ref가 제거된 이유


1. std::mem_fun, std::mem_fun_ref

  • std::mem_funstd::mem_fun_refC++98에서 도입된 함수 어댑터로, 멤버 함수를 호출하는 함수 객체를 생성하는 기능을 제공했습니다.
  • 하지만 이들은 점차 시대에 뒤떨어진 기능이 되었고, C++17 표준에서 제거되었습니다.
  • 그 이유와 더불어 C++98 스타일과 현대적인 C++17 스타일의 코드를 비교하여 살펴보겠습니다.


2. 제거 이유

  • (1) 표현력 부족과 가독성 저하
    • 사용법이 직관적이지 않고 복잡했으며 코드 가독성을 떨어뜨렸습니다.
  • (2) 강력한 대체 기능의 등장
    • std::bind, 람다(lambda), std::function 등의 도입으로 훨씬 더 강력하고 직관적으로 멤버 함수 호출을 구현할 수 있게 되었습니다.
  • (3) 모호성과 제한성
    • const 멤버 함수와 non-const 멤버 함수 처리 시 혼란을 주거나 오류를 발생시키기 쉬웠습니다.
  • (4) 유지보수의 부담
    • 표준 라이브러리에서 더 이상 필요하지 않은 중복 기능으로 분류되어 관리 비용을 줄이기 위해 제거되었습니다.


3. C++98 코드 예시

  • cpp

      #include <iostream>
      #include <vector>
      #include <algorithm>
      #include <functional> // std::mem_fun 사용을 위해 필요
      
      class MyClass {
      public:
          void print() {
              std::cout << "Hello from MyClass!" << std::endl;
          }
      };
      
      int main() {
          std::vector<MyClass*> vec;
          vec.push_back(new MyClass());
          vec.push_back(new MyClass());
      
          // C++98 스타일 - mem_fun 사용
          std::for_each(vec.begin(), vec.end(), 
     	               std::mem_fun(&MyClass::print));
      
          // 메모리 해제
          for (auto ptr : vec) 
     	    delete ptr;
      }
    


4. C++17 코드 예시 (대체 방법)

  • cpp

      #include <iostream>
      #include <vector>
      #include <algorithm>
      
      class MyClass {
      public:
          void print() const {
              std::cout << "Hello from MyClass!" << std::endl;
          }
      };
      
      int main() {
          std::vector<MyClass> vec;
          vec.emplace_back();
          vec.emplace_back();
      
          // C++17 스타일 - 람다 사용
          std::for_each(vec.begin(), vec.end(), 
     	               [](const MyClass& obj) { obj.print(); });
      
          // 또는 std::bind 사용 (권장도 낮아지고 람다로 대체되는 추세)
          // std::for_each(vec.begin(), vec.end(), 
     	 //   std::bind(&MyClass::print, std::placeholders::_1));
      }
    


5. 정리

  • std::mem_funstd::mem_fun_ref더 이상 직관적이지 않고 모호하며, 강력한 대체 기능이 존재 하기 때문에 C++17 표준에서 제거 되었습니다.
  • 람다 표현식 은 더 읽기 쉽고 범용적이므로 최신 C++에서는 람다를 기본으로 사용하는 것이 좋습니다.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++17에서 std::ptr_fun이 삭제된 이유


1. 삭제된 이유

  • std::ptr_fun함수 포인터를 함수 객체로 변환하는 어댑터 였으며, C++98C++03 시절에는 유용했습니다.

  • 그러나 C++11부터 람다 표현식과 std::function, std::bind가 도입되면서 더 간결하고 직관적인 방법으로 동일 기능을 수행할 수 있게 되었고,

  • 결국 표준 라이브러리를 단순화하고 구식 기능을 정리하기 위해 C++17에서 삭제되었습니다.



2. C++98 코드 예제

  • cpp

      #include <iostream>
      #include <vector>
      #include <algorithm>
      #include <functional> // std::ptr_fun
      
      int doubleValue(int x) {
          return x * 2;
      }
      
      int main() {
          std::vector<int> v{1, 2, 3, 4, 5};
          std::transform( v.begin(), v.end(), v.begin(), 
     	                 std::ptr_fun(doubleValue) ); // 함수 포인터
      
          for (int n : v) {
              std::cout << n << " ";
          }
          return 0;
      }
    

  • std::ptr_fun을 사용해 함수 포인터를 함수 객체로 변환


3. C++17 이후 코드 예제 (람다 사용)

  • cpp

      #include <iostream>
      #include <vector>
      #include <algorithm>
      
      int doubleValue(int x) {
          return x * 2;
      }
      
      int main() {
          std::vector<int> v{1, 2, 3, 4, 5};
          std::transform( v.begin(), v.end(), v.begin(),
                          [](int x) { return doubleValue(x); } ); // 람다
      
          for (int n : v) {
              std::cout << n << " ";
          }
          return 0;
      }
    

  • 람다 표현식을 사용하여 함수 포인터 래핑 없이 더 간결하고 안전하게 처리


4. 결론

  • std::ptr_fun은 과거에는 필요했지만, 현대적인 C++ 기능(람다, std::function, std::bind)으로 완벽히 대체되었습니다.

  • 표준 라이브러리를 단순화하고, 더 직관적이고 안전한 코드 작성을 유도하기 위해 C++17에서 삭제되었습니다.




  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++17에서 std::bind1ststd::bind2nd가 제거된 이유


1. 제거된 이유

  • std::bind1ststd::bind2ndC++98에서 함수 객체의 첫 번째 또는 두 번째 인자를 고정 시키는 기능을 제공했습니다.
  • 하지만 몇 가지 문제점이 있었습니다:
    • 제한된 기능 : 첫 번째나 두 번째 인자만 고정할 수 있음
    • 가독성 저하 : 코드가 복잡하고 직관적이지 않음
    • 타입 추론 어려움 : 가끔 의도한 동작이 명확하지 않음
    • 새로운 대체 기능 등장 : C++11 이후 std::bind와 람다(lambda, 익명 함수)가 도입되면서 동일한 기능을 더 간결하고 유연하게 구현할 수 있게 되었음

  • 결국 이러한 이유로 std::bind1ststd::bind2ndC++17 표준에서 제거 되었습니다.


2. C++98 예제 (std::bind2nd 사용)

  • cpp

      #include <iostream>
      #include <functional>
      #include <vector>
      #include <algorithm>
      
      int main() {
          std::vector<int> numbers = {1, 2, 3, 4, 5};
     	 
          // 3보다 큰 값 찾기
          auto it = std::find_if( numbers.begin(), numbers.end(),
             std::bind2nd( std::greater<int>(), 3 )
     	 );
          
          if (it != numbers.end()) {
              std::cout << "찾은 값: " << *it << std::endl;  // 출력: 4
          }
     	 
          return 0;
      }
    


3. C++17 방식 (lambda 사용)

  • cpp

      #include <iostream>
      #include <vector>
      #include <algorithm>
      
      int main() {
          std::vector<int> numbers = {1, 2, 3, 4, 5};
     	 
          // 3보다 큰 값 찾기
          auto it = std::find_if( numbers.begin(), numbers.end(),
              [](int value) { return value > 3; }
          );
          
          if (it != numbers.end()) {
              std::cout << "찾은 값: " << *it << std::endl;  // 출력: 4
          }
     	 
          return 0;
      }
    


4. 요약

  • std::bind1ststd::bind2nd는 제한적인 기능과 가독성 문제로 인해 C++17에서 제거되었으며, 현재는 std::bind나 람다 함수를 사용하는 것이 권장됩니다.

  • 람다는 가독성과 유연성을 모두 충족시키는 현대적인 대안입니다.




  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

Visual Studio C++ 라이브러리 패키지 매니저 vcpkg 설치 및 자동 업데이트 가이드


1. 왜 vcpkg를 설치하여 사용해야 하나요?

  • vcpkg 는 마이크로소프트에서 제공하는 Visual Studio C++ 라이브러리 패키지 매니저입니다.
    • 복잡한 라이브러리 설치 및 빌드를 자동화하며,
    • vcpkg install [패키지명] 한 줄로 손쉽게 설치 및 의존성 해결이 가능합니다.
    • Visual Studio, CMake와 자연스럽게 연동됩니다.
    • 개발환경 관리가 편리해지며, 라이브러리 충돌 및 수동 설정 오류를 줄일 수 있습니다.

  • 또한 주기적인 업데이트와 관리가 중요한 환경에서는 자동 업데이트 설정 을 통해 최신 안정성과 보안 패치를 항상 유지할 수 있습니다.


2. vcpkg 설치 및 자동 업데이트 설정 방법


2.1. vcpkg 설치 방법

  • 2.1.1. PowerShell을 관리자 권한으로 실행합니다.

  • 2.1.2. vcpkg를 설치할 경로로 이동합니다. (예: C:\devtools)
    • powershell

        cd C:\devtools
      
    • 설치 경로는 원하시는 경로로 설정하시면 됩니다.

  • 2.1.3. Git에서 다운로드 및 빌드
    • powershell

      # 소스 코드 얻기
      git clone https://github.com/microsoft/vcpkg.git
      
      # 빌드
      cd vcpkg
      .\bootstrap-vcpkg.bat
    
    • ⚡ 소스를 얻기 전에 Windowsgit 이 설치되어 있어야 합니다.
    • ⚡ 빌드 전에 Visual Studio (C++)가 설치되어 있어야 합니다.

  • 2.1.4. 시스템 환경 변수 PATHC:\devtools\vcpkg 추가

  • ⚡ 빌드와 설치만 필요하시면 여기까지만 하시면 됩니다!!


2.2. 자동 업데이트 스크립트 작성

  • 자동 업데이트 파워쉘 스크립트
    • 예를 들어 C:\devtools\vcpkg\vcpkg_auto_update.ps1 :
    • powershell

        # vcpkg 경로 지정
        $VcpkgPath = "C:\devtools\vcpkg"
        
        # vcpkg 디렉터리 이동
        cd $VcpkgPath
        
        # 최신 버전 가져오기
        git pull origin master
        
        # 업데이트 시 vcpkg 재빌드
        .\bootstrap-vcpkg.bat
        
        # (선택) 설치된 패키지 업그레이드
        .\vcpkg upgrade --no-dry-run --keep-going
      


2.3. 자동 실행 스케줄러 등록 방법

2.3.1. (방법 1) 수동 설정

  • 2.3.1.1. Windows에서 작업 스케줄러 실행

  • 2.3.1.2. 작업 만들기 → 이름: vcpkg Auto Update

  • 2.3.1.3. 트리거: Windows 시작 시

    • 윈도우즈 시작 시에 자동 업데이트를 합니다.
    • 다른 시간대에 업데이트 하셔도 됩니다. (야간, 주말 업데이트 등)
  • 2.3.1.4. 동작:

      프로그램: powershell.exe
      인수: -ExecutionPolicy Bypass -File "C:\devtools\vcpkg\vcpkg_auto_update.ps1"
    
  • 2.3.1.5. 조건 → 네트워크 연결 시 실행 추천

  • 2.3.1.6. 저장 후 완료



2.4. 자동 등록 스크립트 및 레지스트리 파일

2.4.1.️ (방법 2) PowerShell로 자동 스케줄러 등록

  • 예를 들어 다음과 같은 스크립트를 만들어서 실행합니다.

    • register_vcpkg_task.ps1
    • powershell

        $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument '-ExecutionPolicy Bypass -File "C:\devtools\vcpkg\vcpkg_auto_update.ps1"'
        $trigger = New-ScheduledTaskTrigger -AtStartup
        $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
        Register-ScheduledTask -TaskName "vcpkg Auto Update" -Action $action -Trigger $trigger -Principal $principal -Description "자동으로 vcpkg 업데이트 확인 및 적용"
      
  • ⚠ 관리자 권한이 있는 PowerShell에서 실행하세요.



2.4.2.️ (방법 3) 레지스트리 등록 (대안)

  • 예를 들어 다음과 같은 레지스트리 파일을 만들어서 실행합니다.

  • vcpkg_autoupdate.reg 파일 내용:

  • reg

      Windows Registry Editor Version 5.00
      
      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
      "vcpkg_auto_update"="powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File \"C:\\devtools\\vcpkg\\vcpkg_auto_update.ps1\""
    
  • ⚠ 관리 편의성은 작업 스케줄러가 더 좋으며, 레지스트리는 필요할 때만 사용하는 것이 좋습니다.



2.5. 자동 업데이트 등록 방법 요약

  • 방법 특징
    작업 스케줄러 수동 등록 안정적이고 관리 편함. 추천 방법.
    PowerShell 스크립트 자동 등록 한 번 실행만으로 작업 스케줄러 자동 생성. 관리자 권한 필요.
    레지스트리 등록 가장 간단하지만, 제어 및 상태 관리가 어려움. 부팅 속도 및 충돌 가능성 존재.



  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형
반응형

C++ 표준에서 제거된 주요 항목 목록

  • C++ 표준은 지속적으로 발전하며, 새로운 기능이 추가되는 만큼 기존 기능 중 일부는 더 이상 필요하지 않거나 시대에 맞지 않아 점차 폐지 및 제거됩니다.
  • 이러한 변화는 코드의 안전성과 효율성을 높이고, 최신 패러다임에 맞춘 프로그래밍을 유도하기 위한 것입니다.
  • 본 글에서는 C++ 표준에서 폐지 및 제거된 주요 항목과 그 이유, 그리고 대체 기능들을 정리하여 앞으로의 개발에 도움이 될 수 있도록 안내합니다.


1. C++98 도입 후 제거된 항목

  • 항목 도입 폐지 제거 대체/설명
    std::auto_ptr C++98 C++11 C++17 std::unique_ptr , 설명
    std::bind1st, std::bind2nd C++98 C++11 C++17 std::bind / 람다 , 설명
    std::ptr_fun C++98 C++11 C++17 람다 , 설명
    std::mem_fun, std::mem_fun_ref C++98 C++11 C++17 std::function / 람다 , 설명
    std::unary_function, std::binary_function C++98 C++11 C++17 람다 및 함수 객체 , 설명
    std::random_shuffle C++98 C++14 C++17 std::shuffle , 설명
    std::iterator (base 클래스) C++98 C++17 C++20 iterator_concepts 기반, 설명
    std::allocator<void> C++98 C++17 C++20 필요 없어짐, 템플릿 자동 추론. 설명


2. C++11 도입 후 제거된 항목

  • 항목 도입 폐지 제거 대체/설명
    std::result_of C++11 C++17 C++20 std::invoke_result, 설명
    std::tr1 네임스페이스 C++11 C++17 C++20 C++11 이상 표준 기능으로 통합, 설명
    std::function::target_type::operator==
    (특정 연산자 오버로드)
    C++11 C++20 C++23 사용 권장하지 않음, 비교 대신 target_type() 확인, 설명


3. 향후 제거 예정인 항목

  • 항목 도입 폐지 제거 대체/설명
    std::raw_storage_iterator C++98 C++20 C++26 예정 사용 권장하지 않음, 컨테이너 기반 관리 권장
    <codecvt> 일부 인코딩 변환 관련 기능 C++11 C++17 C++26 예정 플랫폼 별 대안 또는 다른 라이브러리 사용 권장


4. 용어

  • 구분 의미 사용 가능 여부 경고 또는 오류 다른 대안 필요 여부
    폐지(deprecated) 앞으로 제거 예정, 사용 권장 안 함 가능 경고(warning) 필요
    제거(removed) 표준에서 완전 제거 불가능 오류(error) 반드시 필요


5. 참조 표준




  • 도움이 되셨으면 하단의 ❤️ 공감 버튼 부탁 드립니다. 감사합니다! 😄

728x90
반응형

+ Recent posts