본문으로 바로가기

C++20) Concepts ( 콘셉트, 개념 ) - 4

category C++/Modern 2020. 11. 29. 17:02

Concepts: Generic Programming의 미래

모든 내용은 Bjarne Stroustrup 교수님의 Good_Concepts에서 발췌하였습니다.

 

6. 개념 오버로딩 ( overloading )

 

generic programming은 다른 타입에 대한 동일한 작업에 동일한 이름을 사용할 수 있습니다. 따라서 overloading이 필수적입니다.

overloading을 할 수 없는경우, 다른 해결방법이 있습니다. ( traits, enable_if 또는 도우미 함수 ) 

Concepts는 주어진 인수의 속성에 따라 함수 중에서 선택할 수 있게 해줍니다.

 

예를 들어, 표준 라이브러리의 std::advance의 간단한 버전을 생각해 봅시다.

template <typename Iter> void advance(Iter p, int n);

advance의 다른 버전도 필요합니다

  • forward_iteartor를 위한 한 번에 한단계 씩 순차적으로 진행하는 advance
  • random_access_iterator를 위한 한 번의 작업에 원하는 위치로 이동하는 advance 

이러한 컴파일 타임 선택은 generic code의 실행을 위해 필수적입니다.

전통적으로, 헬퍼 함수와 태그 디스패치( Tag Dispatch )를 통해 구현했지만, Concepts를 사용하면 간단하고 분명합니다.

void advance(Forward_iterator p, int n) { while(n--) ++p; }
void advance(Random_access_iterator p, int n) { p+=n; }

void use(vector<string>& vs, list<string>& ls)
{
    auto pvs = find(vs,"foo");
    advance(pvs,2); // use fast advance
    auto pls = find(ls,"foo");
    advance(pls,2); // use slow advance
}

컴파일러는 어떻게 올바른 advance를 호출하는 방법을 알아낼까요?

iterator들의 계층을 정의하지도 않았고, 태그 디스패치도 하지 않았습니다.

 

Concepts에 기반한 overloading 해결은 간단합니다.

  • 함수가 단 하나의 개념 요구 사항만 일치하는 경우, 호출합니다.
  • 함수가 단 하나의 개념 요구 사항'도' 일치하지 않는경우, 에러입니다.
  • 함수가 두 개의 개념 요구 사항과 일치하는 경우, 한 개념의 요구사항이 다른 개념 요구사항의 하위 집합( subset )인지 확인합니다.
    - 만약 하위 집합이라면, 가장 많은 요구사항( 가장 엄격한 요구사항 )을 가진 함수를 호출합니다.
    - 만약 하위 집합이 아니라면, 모호하므로( ambiguous ) 에러입니다. 

예시를 보면, Random_access_iteratorForward_iterator보다 더 많은 요구사항을 가지고 있습니다. ( 즉, 더 엄격한 요구사항입니다. )

그래서 Random_access_iterator가 일치하는 std::vector는 더 빠른 advance를 선택합니다.

std::list의 경우, Forward_iterator만 일치하므로, 느린 버전의 advance를 선택합니다.

 

Random_access_iteratorForward_iterator보다 더 엄격합니다. 그 이유는 Forward_iterator를 포함하여 operator[] 및 operator+같은 추가 연산자를 수행하기 때문입니다.

 

엄격함을 위한 개념의 정확한 비교와 관련된 몇 가지 기술들이 있지만, overloading을 위해 그 기술들이 들어갈 필요는 없습니다.

중요한것은 Concepts들 사이에서 '상속 계층'을 명시하거나, 특정 클래스를 정의하거나, 태그 디스패치 헬퍼를 추가할 필요가 없다는 것입니다. ( SFINAE )

컴파일러는 우리를 위해 '진짜 계층( real hierarchies )' 을 계산합니다. 이것은 더 간단하고, 더 유연하며, 오류 발생 가능성이 적습니다.

 

Concepts-based overloading은 generic code와 meta programming code에서 많은 양의 상용구를 제거합니다. ( 대부분 enable_if 사용 )

여기서 일반적인 원칙( general principle )은 프로그래머가 컴파일러에게 더 적합한 일을 하도록 강요하지 않는 것입니다.

Concepts-based overloading을 통해 코드는 서로 다르고 잠재적으로 미묘한 구현 세부사항 ( 예 : 어떤 타입의 속성에 기초하여 overloading을 표현할 때, enable_if의 양( positive )과 음( negative )의 형태를 모두 사용하는 것을 기억하는 것과 같은 )

을 갖는게 아닌, 일반적이고 널리 사용되는 해결 규칙을 따르도록 보장합니다.

 

한 가지 분명한 질문 : 구문론적( syntactically )으로는 동일하지만 의미론적( semantics )으로 다른 유형을 어떻게 구별할 것인가?

표준의 예로써는, Input_iteratorForward_iteartor이고 반복되는 순회에서만 Forward_iteartor가 허용된다는 것입니다.

 

가장 간단한 대답은 '타입 중 하나에 연산을 추가하여 구별할 수 있게 하라' 입니다. 

좀 더 관습적이고 복잡한 대답은 '특성 ( traits ) 클래스를 사용하라' 입니다. 후자는 우리가 어떤 타입도 구별할 수 없을 때 하는 것입니다.

 

예를 들어, Input_iteratorForward_iterator의 경우, Input_iterator는 순회만 가능하고 복사할 수 없기 때문에,

( std::is_copy_constructible 을 사용 ) 실제로 구별할 수 있었습니다. 

 

7. 짧은 형식의 표기법

 

디자인 목표 중 하나는 C++을 단순하게 만드는 것입니다. 따라서 속기 표기법( 3 단락 )을 사용하여 반복적인 작성을 방지하고, 요구 사항을 보다 간결하게 설명합니다.

 

즉,

template <typaname Sequence>
    requires Sortable<Seq>
void Sort(Seq& s);

이를

template <Sortable Seq>
void Sort(Seq& s);

이렇게 줄일 수 있습니다.

그러나 이 코드는 여전히

void sort(Sortable& s);

이와같은 '일반적인 non-generic'에 대한 이상적인 동등성을 얻지 못합니다.

 

이를 위해 추가 '재작성 규칙'이 있습니다. 간단합니다.

가장 간단한 경우에는 가장 짧은 형식을 사용합니다. 더 복잡한 표현이 필요할때, 즉 '둘 이상의 템플릿 인수를 포함하는' 요구사항이 필요할 때는 

template <Sequence S, typename T>
requires Equality_comparable<Value_type<S>, T>
Iterator_of<S> find(S& seq, const T& value);

이와 같이 작성합니다.

 

귀찮게 왜? 이런 긴 형식은 대부분의 코드에대해 불쾌할 정도로 장황하며, 템플릿 구문의 장황함( 'heaviness' )은 사용자들로부터 끊임없는 불폄의 원인이 됐습니다.

그러나, 복잡한 요구 사항을 표현하는 긴 형식이 필요합니다 : 짧게 만든 요구사항은 완벽하게 '일반적( generic )'이지 않습니다.

 

이 디자인은 ' onion principle '을 따릅니다. 기본적으론 짧고 간결하게 작성하세요.

그러나, 간단하게 표현할 수 없을 땐 ' onion principle ' 처럼 한 layer씩 떼 나가야 합니다.

각 층은 당신에게 더 많은 유연성을 추가해주고, 더 많이 울게( cry ) 할 것입니다. ( 추가된 일과 실수들 때문에 )

 

구식의 for 반복문( 완전히 일반적인 )과 range-for 반복문( 더 간단하고 오류가 덜 발생하는 )은 그 원리의 또 다른 예 입니다.

 

세 가지 형식은 함수에 대해 말하는 방식과 일치합니다.

// 템플릿 인수는 type이여야 하며,
// 타입은 반드시 sequence여야 합니다
// s는 해당 타입에 대한 참조여야 합니다.
template <typename Seq>
requires Sequence<Seq>
void algo(Seq& s);

// 템플릿 인수는 Sequence 여야 하며,
// s는 해당 타입에 대한 참조여야 합니다.
template <Sequence Seq>
void algo(Seq& s);

// s는 Sequence에 대한 참조여야 합니다.
void algo(Sequence& s);

이유가 없는 한, 더 짧은 형식을 사용합니다.

 

7.1 인수로 auto 사용

 

짧은 형식은 제약없는 변형도 제공합니다.

void f(auto x);

즉, auto는 가장 제약이 적은 Concepts입니다. 먼저 인수 및 반환 유형에 대해 auto를 제안했고, C++14에는 lambda에도 지원합니다.

concept Any = true; // 모든 타입은 Any입니다.
void g(Any x); // 어떤 타입이든 가져옵니다.

제약이 없는 인수를 지정하는 이 두 가지 방법은 다를 수도 있습니다.

void ff(auto x, auto y); // x와 y는 다른 유형일 수 있습니다.
void gg(Any x, Any y);   // x와 y는 동일한 유형이여야 합니다.

 즉, gg는 STL iterator 쌍과 같이, '동일한 유형의 인수집합' 을 의미합니다.

반면, ff는 전혀 관계가 없는 템플릿 인수를 나타냅니다. 두 스타일 모두 유용하고 흔합니다.

 

void user(vector<string>& vs, list<double> ld)
{
    ff(&vs, &ls); // OK
    gg(vs.begin(), vs.end()); // OK, 쌍 반복자는 같은 타입임
    gg(vs.begin(), ld.end()); // ERROR, 쌍 반복자는 다른 타입임
}

 

7.2 가독성

 

나는 Concepts를 사용하는 사람들이 개념의 표현력과 개선된 오류 메세지를 칭찬하기를 기대했습니다.

그런 측면들이 ( 학생과 전문 개발자들에 의해 ) 언급되었지만, 사람들은 개념을 이용한 코드의 가독성이 엄청나게 향상되었음을 거듭 강조했습니다.

근본적으로 Concepts는 더 나은 인터페이스 사양을 가능하게 하고 좋은 인터페이스는 이해를 단순화하게 합니다.

 

다음과 같이 세 가지 측면이 강화되었다고 들었습니다.

  • 선언문이 더 정확하고 유익해 졌습니다. Concepts를 사용한 선언문은 '완전히 일반적 유형과 코멘트에 대한 서술적 이름을 사용하는 선언' 보다 읽기 쉽고 신뢰성이 높습니다. 또한 Concepts를 사용한 선언문은 '제약 조건을 문서화 하는데 사용되는 주석' 보다 더 짧습니다.
  • call point에서 auto를 Concepts로 대체하면, a의 특성에 대한 불확실성이 제거됩니다.
    예를들어 if (auto = foobar(z)) 는 if(InputChannel x = foobar(z)) 보다 훨씬 읽기 어렵습니다.
  • Concepts는 읽을 수 없는 해결책과 복잡한 boilerplate를 제거합니다. 즉 
    종종 이해하기 위해 템플릿의 정의와 매크로를 조사해야합니다. ( 이해하기 위해, 디버그 하고 개선하기 위해서 )
    해결책은 종종 인터페이스로 블리딩 됩니다. ( 예 : enable_if의 형태로 )

분명 이러한 관찰들은 실험적이기보다는, 개인의 주관적인 판단입니다만 중요하다고 생각합니다.

프로그래머의 미적 판단이 중요합니다.

가독성의 문제가 디자인 및 유지 관리향상에 기여한다고 생각합니다.

 

8. 언어 디자인 질문

 

사랃믈이 언어 디자인에 대해 자주 묻고 때로는 제안하기 때문에 이 섹션을 추가했습니다.

이 글은 언어 설계 문서가 아니므로, 여기서의 논의는 간략합니다. 

  • 언어 디자인 결정에 대한 자연스러운 호기심 충족
  • 명백한 대안이 고려되었음을 잠재 사용자들에게 확신 시켜준다.
  • 디자인이 C++의 generic 디자인 원칙을 어떻게 따르는지 보여줍니다.

8.1 정말 Concepts가 필요한가요?

 

템플릿의 설계와 사용의 초기부터 나와 다른 사람들은 언어 지원 없이 템플릿 자체를 사용하여 어떤 형태의 인터페이스 점검이 표현될 수 있다는 것을 깨달았습니다.

Boost의 concept check가 예시입니다.

오늘날 static_assert ,constexpr 함수, constexpr if, 표준 라이브러리의 type traits, 템플릿 메타프로그래밍의 기술들, 많은 타입들의 변수들들을 듣습니다.

 

일반적으로 달성되는 것은 이상에 못 미치는 인스턴스화 시간 확인으로 이어지고 이는 이상적이지 않습니다.

C++은 단순한 어셈블리가 아닙니다.

  • Concepts는 컴파일 타임에 덕 타이핑을 해야하는 전문가를 위한 고급 기능이 아닙니다.
  • Concepts는 type traits 및 enable_if에 대한 단순한 syntactic sugar가 아닙니다.
  • Concepts는 이상적인 세계에서 존재해야할 기본 기능입니다. 모든 템플릿의 사용을 위한 기초입니다.

만약 1990년에 Concepts가 있었다면, 템플릿과 템플릿 라이브러리가 훨씬 더 간단해졌을 겁니다.

 

8.2 정의 확인

 

Concepts는 현재 템플릿이 요구 사항에 지정되지 않은 작업을 사용하는 것을 막지 않습니다. 예를 들어

template <Number N>
vodi algo(vector<N>& v)
{
    for( auto& x : v ) x %= 2;
}

Number 에는 operator%=를 요구하지 않습니다. 따라서 algo의 호출이 성공하는지는 Concepts에 의해 확인되는 것뿐만 아니라, 인수 유형의 실제 속성에 따라 달라질 것입니다.

-> 실제 타입이 operator%=를 제공하지 않는다면, 인스턴스화될 시점에 error가 날 것 입니다.

 

일부 사람들은 이것을 심각한 오류라고 생각합니다. 하지만 그렇지 않습니다. 템플릿의 Concepts와 비교하여, 템플릿 정의를 확인하지 않은 것은 의도적인 설계 선택이였습니다.

우리( 여기서 우리는 사용자가 아닌 '표준 위원회' ) 는 현재 지정된 Concepts로 정의 확인을 구현하는 방법을 알고 있습니다.

우리는 분석과 실험을 했습니다. 그러나 우리는 매우 의도적으로 초기 concepts 설계에 그러한 특징을 포함하지 않도록 결정했습니다.

  • 초기 설계를 지연( delay )하고 복잡( complicate )하게 만들고 싶지 않았습니다.
  • 우리는 개념의 이점의 90%가 향상된 사양과 인스턴스화가 될 때의 검사가 아닌, 코드 작성 시점의 가치에 있다고 추정합니다.
  • 템플릿 구현자는 정상적인 테스트 기술을 통해 제공할 수 있습니다.
  • 언제나 그렇듯이, 형식상의 오류는 항상 불편할 정도로 늦게 포착됩니다.
  • 정의를 확인함으로써 구형의 제약되지 않은 코드에서 Concepts 기반 템플릿으로 변환할 때, 복잡하게 만들 수 있다.
  • 정의를 확인함으로써, 우리는 디버깅 보조장치, 로깅 코드, 성능 카운터 및 기타 ' 스캐폴딩 코드' 를 인터페이스에 영향을 주지않고 제공할 수 있습니다.

또한 이 두가지가 가장 중요합니다.

  • 일반적인 템플릿은 구현시 다른 템플릿을 호출합니다.
    Concepts를 사용하는 템플릿이 그렇지 않은 라이브러리에서 템플릿을 호출할 수 없는경우, Concepts를 가진 라이브러리는 라이브러리가 지원하기 이전 라이브러리를 사용할 수 없습니다.
    이건 특히 두 개의 라이브러리를 사용하여 한개 이상의 조직에서 개발, 유지, 사용될 때 심각한 문제입니다.
    개념의 점진적인 채택은 많은 코드 기반에서 필수적입니다.
  • 'Scaffolding code' ( 템플릿과, 논템플릿 모두 다 ) 는 매우 흔하며, 라이브러리의 life-time동안 계속 변경됩니다.
    만약 인터페이스가 logging을 사용하기 위해 번경되어야 한다면, 우리는 첫째로 말했던 것과 동일한 유지보수 문제를 가지고 있습니다.

이러한 제약된 템플릿( Concepts를 사용하는 템플릿 ) 은 제약없는 템플릿( Concepts 를 사용하지 않는 템플릿 )을 호출할 수 있습니다.

이 경우 인스턴스화 시간까지 구현 오류가 발견되지 않습니다. 마찬가지로 기존 템플릿( unconstrained )은 제한된 템플릿을 호출할 수 있습니다. 이경우, 사용 오류는 인스턴스화 시간까지 찾을 수 업습니다.

전자는 Concepts들이 하향식( top down )으로 도입될 수 있다는 것을 암시하는 반면, 후자는 대부분의 이익( benefit )이 그렇게 함으로써 발생한다는 것을 암시합니다.

 

그래서 우리는 '정의 확인'을 수행하는 방법을 알고 있지만, 이런 두 가지 문제를 해결하기 위해 추가하지 않았습니다.

C++의 설계는 '모든 것을 방지하는 것보다 유용한 기능을 허용하는 것이' 더 중요합니다.

이것은 오늘 날 C++을 다른 많은 언어와 구별하는 규칙 중 하나입니다.

 

8.3 템플릿 분리 컴파일

 

템플릿의 완전한 분리 컴파일이 추가 된다면 많은 간접 함수 호출이 포함되어 성능이 저하됩니다.

명백한 대안은 모듈 시스템의 일부로 반 컴파일 된 템플릿을 사용하기 바랍니다.

 

8.4 다중 표기법

 

Concepts는 여러 가지 표현 방법을 제공합니다. 이는 이상적이라고 생각합니다.

그러나 '방법이 한 가지 밖에 없다' 라고 생각하는 사람들이 있습니다. 그러한 단순성은 표현성은 한계( 예 : 짧은 개념만 사용 ) 또는 복잡함( 예 : 긴 개념만 사용 )을 초래합니다.

 

개념은 템플릿 변수 또는 템플릿 함수로 정의할 수 있습니다. 예를 들면

 

template <typename T>
concept Eq1 = 
    requires(T a, T b) {
        { a == b } -> std::same_as<bool>;
        { a != b } -> std::same_as<bool>;
    };
    
template <typename T>
concept Eq2() {
    return requires(T a, T b) {
        { a == b } -> std::same_as<bool>;
        { a != b } -> std::same_as<bool>;
    };
}

불행히도, 템플릿 변수를 사용하는 구문과 템플릿 함수를 호출하는 구문 기능은 다릅니다.

이것은 Concepts와는 관련이 없습니다만

template <typename T> requires Eq1<T> void f(T&);
template <typename T> requires Eq2<T>() void g(T&);

() 추가 여부를 기억 해야하는 것은 성가신 일입니다. 그러나 C++에서 함수를 호출하려면 ()가 필요하고, 변수는 그렇지 않습니다.

변수와 함수를 모두 허용하는 이유는 generality 때문입니다. C++에서 표현식이 정의되는 방식에서 따랐습니다.

 

8.5 Opt in

 

Concepts 설계는, 컴파일러가 이미 알고 있는 것 ( 그리고 종종 프로그래머보다 더 잘 알고 있는 것 )을 프로그래머에게 강요해서는 안 된다는 원칙을 따릅니다.

이로 인해 코드가 짧고 깨끗하며 오류가 감소합니다.

  • Concepts 기반 overloading을 사용하지 않습니다.
    이것은 장황하지 않고, C++의 정신으로 일관됩니다. 몇몇 사람들은 traits를 추가하는 것에 익숙해져 있습니다.
    충분히 매력적이지만, 그것은 기본적으로 해결책이고 'generic programming' 과 'ordinary programming'의 차이입니다. 
  • Concepts 사이 관계 계층 구조를 선택하거나 명시적으로 정의하지 않는다.
    컴파일러는 개념 간의 적절한 관계를 계산하고, 매우 간단한 해결 메커니즘 ( 6 단락 ) 을 사용합니다.
    명시적 개념 계층 구조의 사양을 요구하면, 유연성을 제한하고 Concepts가 여러 계층 구조의 일부가 되도록 실현 가능해야 하며
    결국 여러 계층 구조 및 라이브러리 디자이너의 더 많은 예측력( foresight ) 이 필요해집니다.
  • 개념에 맞는 타입을 택하지 않는 경우 : 형식에 요구하는 속성이 있는 경우, 개념에서 요구하는 것과 일치합니다. 즉, '모델링 선언'을 위한 코드를 가지고 낭비할 필요가 없습니다.
    ( 예를 들면, vector<T>는 Container 이다. List<T>::Iterator는 Bidirectional_iterator 이다. 같은 )
    ( 컴파일러가 확인하지 않는 한 ) 라이브러리 사용을 복잡하게 만들도록 요구하는 것은 오류의 원인이 될 수 있으면 또한 라이브러리 설계자로부터 더 많은 예측을 필요로 할 수 있습니다.

두 개념이 두문적으로 동일하지만 의미가 다른 경우 ( 예 : Input_iterator와 Forward_iterator )

명확화 : operations 또는 member type을 추가하여 명확하게 하거나, 특성 클래스에 대한 작업을 구현해야 합니다.

타입이 개념을 충족하는지 확인하려면 static_assert를 사용하세요.

 

8.6 템플릿을 찾을 수 없음 ( Can't spot the templates )

void sort(Sortable&);

일부 숙련된 C++ 프로그래머는 이것이 템플릿이라는 구문상의 단서가 없다고 생각합니다.

하지만, 연산자에 대한 속성이 ' 영원히' 있다는 점에 유의하세요. 이 표기법을 사용하여 혼란스럽거나 나쁜 코드를 작성하지 않습니다.

수십 년 동안 C++이 확립된 영역에서, 개념과 같은 새로운 기능은 기존 기능보다 사용하기가 더 간단합니다.

 

이를 뒷받침하기 위해, 'erasure' 라는 것을 이미 도입했습니다.

예를들어

std::pair p{9.2, 4};
std::pair<double, int> p {9.2, 4};

이 둘은 동일합니다. 이름을 변경하라는 제안또한 듣지 못했습니다.

템플릿은 사용자에게, 자신이 템플릿임을 알립니다.

 

8.7 Concepts는 일종의 클래스여야 합니까?

 

다른 언어에 대한 경험과 C++0x 개념에 대한 실험을 바탕으로, 일부 사람들은 Concepts가 클래스와 유사하게 정의되어야 한다고 생각하게 되었습니다. ( 목록으로 선언되기 때문에 )

실제로 Signature 방면으로, Concepts를 정의할 수 있습니다.

이 기술을 권장하진 않지만, 여기선 Base에서 파생되는 데 필요한 유형의 개념을 정의할 수 있는 한가지 방법입니다.

template<class T>
concept Hack =
 requires(T t, Base* p, int(T::*pp)(double), int* ppp) {
 { &t==std:addressof(t) }; // make sure operator & isn’t overloaded in a nasty way
 { p = &t }; // T is derived from Base
 { pp = &T::f }; // T has a member int f(double)
 { ppp = &t.m}; // T has a member int m
 };

이것은 overloading을 다루지 않지만, Signature를 기반으로 한 많은 언어가 overloading 을 잘 처리하지 못하므로,

결국 이것은 우리가 일반적으로 추천하는 기술이 아닙니다.

 

8.8 Concepts는 Traits 클래스가 아닙니다.

 

C++ Concepts는 0 개 이상의 템플릿 인수 타입에 대한 컴파일 시간 조건자 입니다.

  • Concpets에 대한 연산자의 요구는 함수 Signature가 아닌, 유효한 표현식의 관점에서 지정됩니다
  • Concepts는 함수를 포함하여, 일반 술어로 지정됩니다. ( bool을 반환함 )
  • Concepts와 일치하기 위해 타입을 명시적으로 정의할 필요가 없습니다. 일치가 추론됩니다.
  • Concepts는 값 인수( arguments )를 사용할 수 있습니다. ( 단순한 타입 인수가 아닙니다. )
  • Concepts는 많은 인수를 가질 수 있습니다.
  • Concepts 함수는 overloading이 가능합니다.
  • algorithm은 Concepts 안에서 overloading 될 수 있습니다.
  • Concepts는 기본적인 구현 ( default constrain implementations )을 제약하지 않습니다.
  • 개념은 계층의 멤버로 정의되지 않습니다. 개념들 간의 관계는 추론됩니다.
  • 개념은 템플릿의 템플릿 인수를 제한할 수 있습니다.

이것은 유형 클래스 ( 예 : Haskell의 유형 클래스 ) 와 다릅니다. 

 

결론

 

개념은 원래 구상된대로 C++ 템플릿을 완성합니다.

 

개념은 사용 및 정의가 매우 간단합니다. 그들은 genericcode의 개선에도 도움이 됩니다. 하지만 효과적인 사용을 위해 언어 기술 세부 사항뿐만 아니라 원칙을 이해해야합니다. 그 점에서 개념은 다른 기본 구조 ( 함수, 클래스 및 템플릿 같은 )와 유사합니다.

제한되지 않은 템플릿에 비해 '실행' 이 업슷ㅂ니다. 즉 개념을 사용하여 발생하는 시간 오버헤드가 없습니다.

 

개념은 C++에 적합하고 C++의 디자인 원칙을 따르도록 신중하게 설계되었습니다.

  • 좋은 인터페이스 제공 ( 이게 초반에 말한 well-specified 인터페이스를 뜻하는 것 같음 )
  • 의미적 일관성( semantic coherence )를 가짐
  • 사용자가 기계 친화적으로 코드를 작성하지 않아도 됨
  • 단순한 일을 단순하게 처리할 수 있음
  • 오버헤드가 없음

친순함과 단순함을 혼동하지 마세요.  장황한 것과 '이해하기 쉬운' 것을 혼동하지도 마세요.

개념을 사용해 보세요. 그것은 당신의 generic programming을 극적으로 향상시키고 현재 해결 방법들 ( traits class ) 이나 저수준 기술 ( enable_if 기반 오버로딩 ) 이 오류가 발생하기 쉽고 지루한 어셈블리 프로그래밍처럼 느껴지게 만들것입니다.

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

C++20) Modules ( 모듈 ) - 2  (5) 2021.03.14
C++20) Modules ( 모듈 ) - 1  (0) 2021.03.14
C++20) Concepts ( 콘셉트, 개념 ) - 3  (0) 2020.11.28
C++20) Concepts ( 콘셉트, 개념 ) - 2  (0) 2020.11.27
C++20) Concepts ( 콘셉트, 개념 ) - 1  (0) 2020.11.25