쿨보이 원 프로그래밍

한동안 프로그래밍에 대한 전체적인 정보를 발표하고 싶었습니다.지금까지 나는 정말 내가 재미있다고 생각하는 댓글이 없다.그러나 지난 학기에 저는 한 과목을 배웠습니다. 이 과목에서 저는 C++를 최종 프로젝트의 주요 도구로 사용해야 합니다.이 수업을 듣기 전에, 나는 C++에 대해 아는 것이 매우 적다.요 몇 주 동안 저는 C++의 재미있는 특성을 배웠습니다. 저는 댓글을 통해 여러분과 공유하고 싶습니다.

문제.
나의 프로젝트는 C++로 일부 세포 자동기를 설계하고 시뮬레이션하는 것이다.대략적으로 말하면 세포 자동기는 전역 규칙에 따라 자기 갱신을 하는 대상(명명 세포)이다.타임 스탬프마다 하나의 단원은 전역 규칙과 그 인접 단원에 따라 자신을 업데이트합니다.이런 세포는 통상적으로 세포의 기질을 나타낸다.간단하게 보기 위해 아래의 예에서 이 행렬은 astd::array에 불과하다.즉, 한 세포는 무엇이든지, 어떤 종류의 물체든지, 하나의 세포가 될 수 있다는 것이다.C++의 경우 일반적으로 사용templates이 발생합니다.그러나'이것이 무엇이든 될 수 있다'보다 더 중요한 것은 이 일은 반드시 일반적인 행위를 실현해야 한다는 것이다.특히to_string 방법.모든 칸의 현재 상태를 파일에 쓰기 위해서 그것이 필요합니다.CellularAutomaton 과정의 시작은 다음과 같습니다.
template<uint32_t rows, uint32_t cols>
class CellularAutomaton {

    std::array<???, rows * cols> cells;
};
하지만???유형은무엇일까.

첫 번째 솔루션
모든 세포는 그 유형이 어떻든지 간에 일반적인 행위를 실현해야 한다.우리는 이곳에서 상속을 사용할 수 있다.모든 칸을 계승할 CellBase 클래스를 만듭니다.CellBase는 순전히 하나의 인터페이스로 실례화할 수 없다. 왜냐하면 가상 방법만 있고 실현되지 않았기 때문이다.
class CellBase {
 public:

    CellBase() = default;
    virtual std::string to_string() const = 0;
};
이제 Automaton 클래스의 구현은 다음과 같습니다.
template<uint32_t rows, uint32_t cols>
class CellularAutomaton {

    std::array<std::unique_ptr<CellBase>, rows * cols> cells;

 public:

    std::unique_ptr<CellBase>& getCell(uint32_t row, uint32_t col) {
        return cells[row * cols + col];
    }

};
몇 가지 참고 사항:
  • 우리는 반드시 지침을 사용하여 단원격을 저장해야 한다. CellBase는 실례화될 수 없기 때문이다.
  • vtable로부터 계승된 모든 종류는 하나CellBase가 있을 것이다.
  • 우리가 자동기에서 단원을 가져와야 할 때마다 우리는 유일한 바늘이 복사될 수 없기 때문에 std::unique_ptr<>&로 돌아가야 한다.
  • 우리가 제3자 라이브러리를 도입할 때 이 해결 방안은 불가능하다.

  • 두 번째 솔루션
    템플릿은 하나만 사용하면 됩니다.
    template<typename T, uint32_t rows, uint32_t cols>
    class CellularAutomatonV2 {
     public:
    
        T& getCell(uint32_t row, uint32_t col) {
            return cells[row * cols + col];
        }
    
        std::array<T, rows * cols> cells;
    };
    
    현재 우리의 목표는 어떤 방식으로 단언T하여 같은 서명을 가진 std::string to_string() const {} 방법을 실현하는 것이다.
    간단하게 하나struct를 정의하자. 실제로는 수신 유형과 서명trait이다.이것은 정적 구성원 value 을 저장해서 유형 T 의 서명 실현 has_string 방법을 알려 줍니다.하나의 특질은 본질적으로 이렇다. 그것은 우리에게 유형적인 사실을 알려준다.
    아래의 코드는 이런 특질의 출발점이다.그것은 하나의 유형S과 하나의 함수 서명T을 수신하고 후자도 하나의 유형이다.정적 구성원S이 있습니다. 이 구성원은 "이 유형이 특정 서명을 사용하여 string을 호출하는 방법이 있습니까?"라는 질문에 대한 답을 저장합니다.value에서 우리는 T를 통해 이 방법을 포획하고 서명(이 명칭이 존재한다면)과 &T::method_name을 비교할 수 있다.근데 어떻게 하지?
    template<typename T, typename S>
    struct trait_has_to_string {
        static bool constexpr value = ...;
    };
    

    스파나가 구원하러 가자.
    SFINAE는 교체 실패가 오류가 아니라는 의미입니다.실제로 컴파일러가 템플릿 함수를 보았을 때, 이 함수에 전달되는 유형에 따라 코드를 생성하려고 시도합니다.예를 들면 다음과 같습니다.
    template<typename T, typename U>
    auto add(const T& a, const U& b) -> decltype(a + b) {
        std::cout << "Calling general template" << std::endl;
        return a + b;
    }
    
    template<typename U>
    auto add(const std::string& a, const U& b) -> std::string {
        std::cout << "Calling specialization const std::string&" << std::endl;
        return a + std::to_string(b);
    }
    
    template<typename U>
    auto add(const char* a, const U& b) -> std::string {
        std::cout << "Calling specialization for const char*" << std::endl;
        return std::string(a) + std::to_string(b);
    }
    
    S 호출할 때마다 컴파일러는 세 개의 템플릿 함수를 선택할 수 있습니다.다음 전화 번호:
        const std::string test = "testing";
        std::cout << add(2, 2) << std::endl;
        std::cout << add("testing ", 1) << std::endl;
        std::cout << add(test, 1) << std::endl;
    
    결과:
    Calling general template
    4
    Calling specialization for const char*
    testing 1
    Calling specialization const std::string&
    testing 1
    
    첫 번째 호출은 addadd(const std::string&, const U&) 서명이 있는 템플릿 함수를 사용할 수 없지만, 교체 실패는 오류가 아닙니다. 전달된 형식만 최소한 버전을 호출할 수 있습니다.첫 번째 호출에 대해 가장 일반적인 버전을 선택할 것입니다.두 번째 호출은 두 개의 템플릿 함수(add(const char*, const U&)add(const T&, const U&)에 적합하다.그것은 더욱 구체적이기 때문에 add(const std::string&, const U&)를 선택할 것이다.컴파일러는 항상 가장 구체적인 일치를 시도할 것입니다.세 번째 전화는 예상대로 선택됩니다add(std::string&, const U&).그것만 있으면 우리는 클래스에 방법이 존재하는지 검사할 수 있다.
    그러나 계속하기 전에 첫 번째 템플릿 함수에 add(const char*, const U&) 서명이 있음을 주의하십시오.쿼리 표현식의 유형에 대한 키워드template<typename T, typename U>
    auto add(const T& a, const U& b) -> decltype(a + b)
    위의 성명에서, 이것은 함수가 표현식 decltype 과 같은 형식으로 되돌아오는 것을 의미할 뿐이다.

    문제로 돌아가다
    다음 코드는 우리가 원하는 기교를 실현했다.
    template<typename T, typename S>
    struct trait_has_to_string {
        using True = std::true_type;
        using False = std::false_type;
    
        template<typename U, U> struct Model;
    
        template<typename U> static True Check(Model<S, &U::to_string> *);
        template<typename U> static False Check(...);
    
        static bool constexpr value = std::is_same<True, decltype(Check<T>(0))>();
    };
    
    우리는 이렇게 불러도 된다.
    trait_has_to_string<T, std::string(T::*)() const>::value;
    
    기능 변경:
    template<typename T>
    struct has_to_string {
        static bool constexpr value = trait_has_to_string<T, std::string(T::*)() const>::value;
    };
    
    a+b 방법의 유형은 자신의 서명이다.정적 방법to_string은 두 가지 버전이 있다.실례화True Check(Model<S, &U::to_string>(즉 하나Model와 서명이 완전히 일치하는 경우&U::to_string만 있으면 첫 번째를 호출합니다.S에 전달된 0은 하나Check<T>(0)에 불과하다.첫 번째 버전이 실패하면 가장 일반적인 버전을 호출합니다.코드의 나머지 부분은 매우 간단하다.
  • nullptrstd::true_type는 각각 값std::true_typetrue의 유형이다.
  • 우리는 false의 두 가지 버전을 작성할 수 있었는데 첫 번째 버전은 Check이고 두 번째 버전은 true이다.그러나 그들은 항상 같은 값을 되돌려준다.이러한 귀환 유형을 각각 falsestd::true_type로 전문화하는 것은 의미가 있다.그리고 우리는 심지어 호출std::false_type할 필요가 없다. 왜냐하면 실례화된 버전은 그 반환 값을 방법의 반환 형식으로 하기 때문이다.
  • 이제 automaton의 최종 버전입니다.
    class CellularAutomaton {
     static_assert(has_to_string<T>::value, "T does not implement std::string to_string() const;");
     public:
    
        T& getCell(uint32_t row, uint32_t col) {
            return cells[row * cols + col];
        }
    
        std::array<T, rows * cols> cells;
    };
    
    그리고 두 가지 다른 세포 유형:
    struct CellTypeOne {
        std::string to_string();
    };
    
    struct CellTypeTwo {
        std::string to_string() const;
    };
    
    마지막으로 주요 문제는 다음과 같습니다.
    CellularAutomaton<CellTypeOne,2, 2> automaton1;
    CellularAutomaton<CellTypeTwo,2, 2> automaton2;
    
    현재 Checkstatic_assert로 인해 실패할 것이다. automaton1 수식부호가 방법 서명에 없기 때문이다.이 기능은 이 방법이 없는 형식 const 을 호출하기 전에 오류를 검출하고 내보낼 수 있도록 합니다.이 외에도 이 방법이 실행되지 않은 형식에 대한 기본 반환을 지정할 수 있습니다.

    좋은 웹페이지 즐겨찾기