콘텐츠로 이동

Concepts와 제약 (Concepts and Constraints)

개념을 아껴서 사용하십시오. 일반적으로 개념과 제약 조건은 C++20 이전에 템플릿을 사용했던 경우에만 사용해야 합니다. 헤더가 라이브러리 내부로 표시되지 않는 한 헤더에 새로운 개념을 도입하지 마세요. 컴파일러에 의해 적용되지 않는 개념을 정의하지 마세요. 템플릿 메타프로그래밍보다 제약 조건을 선호하고 template<Concept T> 구문을 피하세요. 대신 requires(Concept<T>) 구문을 사용하세요.

개념 키워드는 템플릿 매개변수에 대한 요구사항(예: 유형 특성 또는 인터페이스 사양)을 정의하기 위한 새로운 메커니즘입니다. require 키워드는 템플릿에 익명 제약 조건을 적용하고 컴파일 타임에 제약 조건이 충족되는지 확인하는 메커니즘을 제공합니다. 개념과 제약조건은 종종 함께 사용되지만 독립적으로 사용될 수도 있습니다.

  • 개념을 사용하면 템플릿이 포함될 때 컴파일러가 훨씬 더 나은 오류 메시지를 생성할 수 있으므로 혼란이 줄어들고 개발 경험이 크게 향상될 수 있습니다.
  • 개념은 컴파일 시간 제약 조건을 정의하고 사용하는 데 필요한 상용구를 줄여 결과 코드의 명확성을 높이는 경우가 많습니다.
  • 제약조건은 템플릿 및 SFINAE 기술로는 달성하기 어려운 일부 기능을 제공합니다.

  • 템플릿과 마찬가지로 개념으로 인해 코드가 훨씬 더 복잡해지고 이해하기 어려워질 수 있습니다.

  • 개념이 사용 사이트의 클래스 유형과 유사하게 나타나기 때문에 개념 구문은 독자에게 혼란을 줄 수 있습니다.
  • 특히 API 경계의 개념은 코드 결합, 강성 및 골화를 증가시킵니다.
  • 개념과 제약 조건은 함수 본문의 논리를 복제할 수 있으므로 코드가 중복되고 유지 관리 비용이 증가합니다.
  • 개념은 여러 위치에서 활용될 수 있고 서로 별도로 발전할 수 있는 독립형 명명 개체이기 때문에 기본 계약의 진실 소스를 모호하게 만듭니다. 이로 인해 명시된 요구 사항과 묵시적인 요구 사항이 시간이 지남에 따라 달라질 수 있습니다.
  • 개념과 제약 조건은 새롭고 명확하지 않은 방식으로 오버로드 해결에 영향을 미칩니다.
  • SFINAE와 마찬가지로 제약 조건으로 인해 코드를 대규모로 리팩터링하기가 더 어려워집니다.

표준 라이브러리에 미리 정의된 개념은 동등한 개념이 존재할 경우 유형 특성보다 선호되어야 합니다. (예를 들어 std::is_integral_v가 C++20 이전에 사용되었다면 std::integral은 C++20 코드에서 사용해야 합니다.) 마찬가지로 최신 제약 조건 구문(requires( Condition ) 을 통해)을 선호합니다. 레거시 템플릿 메타프로그래밍 구성(예: std::enable_if< Condition > )과 template< Concept T> 구문을 사용하지 마세요.

기존 개념이나 특성을 수동으로 다시 구현하지 마세요. 예를 들어, requires(requires { T v; }) 대신 requires(std::default_initialized<T>)를 사용하세요.

새로운 개념 선언은 드물어야 하며 API 경계에 노출되지 않도록 라이브러리 내에서만 내부적으로 정의되어야 합니다. 보다 일반적으로 C++17에서 해당 레거시 템플릿을 사용하지 않는 경우 개념이나 제약 조건을 사용하지 마세요.

함수 본문을 복제하는 개념을 정의하거나, 코드 본문이나 결과 오류 메시지를 읽어 보면 사소하거나 명백해지는 요구 사항을 부과하지 마세요. 예를 들어 다음을 피하십시오.

template <typename T>     // Bad - redundant with negligible benefit
concept Addable = std::copyable<T> && requires(T a, T b) { a + b; };
template <Addable T>
T Add(T x, T y, T z) { return x + y + z; }

개념은 컴파일러에 의해 정적으로 검증 가능해야 합니다. 의미론적(또는 강제되지 않은) 제약에서 주요 이점을 얻을 수 있는 개념은 사용하지 마세요. 컴파일 타임에 적용되지 않는 요구 사항은 대신 주석, 주장 또는 테스트와 같은 다른 메커니즘을 통해 적용되어야 합니다.


이해하기 쉽게 설명하기

핵심: Concepts는 아껴서 사용

Concept와 제약(constraint)은 템플릿 매개변수의 요구사항을 정의하는 C++20 메커니즘입니다. 오류 메시지를 개선하는 등 장점이 있지만, 코드를 복잡하게 만들고 API 결합도를 높이므로 아껴서 쓰세요.

규칙

  • C++20 이전에 이미 템플릿을 쓰던 경우에만 개념·제약을 도입하세요(C++17에서 레거시 템플릿을 안 썼다면 쓰지 말 것).
  • 표준 라이브러리에 있는 개념을 직접 다시 구현하지 마세요. 예: requires(requires { T v; }) 대신 requires(std::default_initialized<T>).
  • 동등한 표준 개념이 있으면 타입 특성보다 개념을 선호(std::is_integral_vstd::integral).
  • 최신 제약 구문 requires(조건)을 선호하고, std::enable_if<>template<Concept T> 구문은 피하세요.
  • 새 개념 선언은 드물게, 라이브러리 내부에만(API 경계에 노출 금지).
  • 함수 본문 로직을 복제하거나, 코드를 읽으면 자명한 요구사항을 부과하는 개념은 만들지 마세요.
  • 개념은 컴파일러가 정적으로 검증 가능해야 합니다. 강제되지 않는 의미론적 제약이 주목적이라면 개념 대신 주석·어설션·테스트로.