I'm FanJae.

[C++ Intermediate] Max Implement 본문

C++/Intermediate

[C++ Intermediate] Max Implement

FanJae 2024. 9. 27. 20:35

1. Max Implement

#include <iostream>
#include <string>

template<class T>
const T& mymax(const T& obj1, const T& obj2)
{
    return obj1 < obj2 ? obj2 : obj1;
}
int main()
{
    std::string s1 = "abcd";
    std::string s2 = "xyz";
    
    auto ret1 = mymax(s1, s2);
    auto ret2 = mymax(s1.size(), s2.size());
        
    std::cout << ret1 << std::endl;  
}

 

1-1. 알고리즘 함수가 사용하는 정책(비교방식)을 변경하는 방법.

① 알고리즘 함수가 사용하는 정책(비교방식)을 변경하고 싶을때 보통 이항 조건자를 사용한다. (C++ STL)

std::sort(v.begin(),v.end(), [](auto &a, auto&b) { return a.size() < b.size(); } );

 

#include <iostream>
#include <string>

template<class T>
const T& mymax(const T& obj1, const T& obj2)
{
    return obj1 < obj2 ? obj2 : obj1;
}
int main()
{
    std::string s1 = "abcd";
    std::string s2 = "xyz";
    
    auto ret1 = mymax(s1, s2);
    auto ret2 = mymax(s1.size(), s2.size());
        
    auto ret3 = mymax(s1, s2, [](auto& a, auto& b) { return a.size() > b.size(); } );
    auto ret4 = mymax(s1, s2, [](auto& a) { return a.size(); } );
                             // f(s1) < f(s2)
    
    std::cout << ret2 << std::endl;
}

단항 조건자를 사용하는 방법

sorted(str_list, key = lambda x : len(x) );

- Python이 사용하는 방식.

 

방법 1과 방법 2를 모두 사용하는 방법

- C++20 Range Algorithm의 원리

- 멤버 함수 포인터, 멤버 데이터, std::invoke 활용.

#include <iostream>
#include <string>

template<class T, class Proj>
const T& mymax(const T& obj1, const T& obj2, Proj proj)
{
    return proj(obj1) < proj(ob2) ? obj2 : obj1;
}

int main()
{
    std::string s1 = "abcd";
    std::string s2 = "xyz";
    
    auto ret1 = mymax(s1, s2, [](auto &a) { return a.size() ; }
    
    std::cout << ret1 << std::endl;
}

- mymax의 3번째 인자로 단항 조건자를 전달한다.

- 비교시 조건자의 결과를 놓고 비교한다.

proj(obj1) < proj(obj2)

- C++20에서 Projection이라고 칭해지는 개념이다.

 

※ 단항 조건자 대신 멤버 함수의 포인터를 보낼 수 없을까?

#include <iostream>
#include <string>
#include <functional>

template<class T, class Proj>
const T& mymax(const T& obj1, const T& obj2, Proj proj)
{
//   return proj(obj1) < proj(ob2) ? obj2 : obj1;
     return std::invoke(proj, obj1) < std::invoke(proj, obj2) ? obj2 : obj1;
}

int main()
{
    std::string s1 = "abcd";
    std::string s2 = "xyz";
    
    auto ret1 = mymax(s1, s2, [](auto &a) { return a.size() ; }
    auto ret2 = mymax(s1, s2, &std::string::size );
    
    std::cout << ret1 << std::endl;
}

- std::invoke()를 사용하면 된다.

proj(obj1) proj가 일반 함수라면 OK.
멤버 함수 라면 Error.
std::invoke(proj,obj1) Proj가 일반 함수 뿐 아니라 멤버 함수라도 Ok.

- projection 인자는 생략할 수 있어야 한다.

- Proj 템플릿 인자의 디폴트 값이 필요하다.

 

1-2. std::identity ( C++ 20 )

#include <iostream>
#include <string>
#include <functional>

template<class T, class Proj = std::identity>
const T& mymax(const T& obj1, const T& obj2, Proj proj = { }) // Default 객체 사용.
{
     return std::invoke(proj, obj1) < std::invoke(proj, obj2) ? obj2 : obj1;
                           // f(obj1)
}

int main()
{
    std::string s1 = "abcd";
    std::string s2 = "xyz";
    
    std::identity f;
    auto& r = f(s1); // 반환 값으로 내 자신의 참조가 나옴.
    
    std::cout << &s1 << std::endl;
    std::cout << &r << std::endl;
    
    auto ret = mymax(s1, s2);
    std::cout << ret << std::endl; 
}

- 전달 받은 인자를 어떠한 변화도 없이 그대로 반환하는 함수 객체

- <functional>

- C++20 부터 지원되지만 C++11 문법으로도 구현가능하다. (단, [[nodiscard]] 는 C++17)

- std::identity 구현에 사용된 기술은 Perfect Forwarding과 Callable에서 다룸.

 

1-3. 멤버 변수 포인터도 적용된다.

#include <iostream>
#include <string>
#include <functional>

template<class T, class Proj = std::identity>
const T& mymax(const T& obj1, const T& obj2, Proj proj = { }) // Default 객체 사용.
{
     return std::invoke(proj, obj1) < std::invoke(proj, obj2) ? obj2 : obj1;
                      // (obj1.*proj) < (obj2.*proj)
}

struct Point
{
    int x, y;
};

int main()
{
    Point p1 = {2, 0};
    Point p2 = {1, 1};
    
    auto ret = mymax(p1, p2, &Point::y ); // y값만 비교하라.
    
    std::cout << ret.x << ", " << ret.y << std::endl;
}

- 멤버 함수 포인터 뿐 아니라 멤버 변수 포인터도 std::invoke를 사용할 수 있다.

projection 생략 mymax(s1, s2);
단항 조건자 mymax(s1, s2, [](auto& a) { return a.size(); } );
 멤버 함수 포인터 mymax(s1, s2, &std::string::size);
 멤버 변수 포인터 mymax(p1, p2, &Point::y);

2. Max Implement 발전

- Callable 관련 내용을 듣고와서 다시 보려고 한다.


3. 

 

포스트를 적다가 앞의 내용부터 다시 한번 보고 오는게 좋을듯 하여 재 복습을 해보려 한다.

Comments