함수에서 C++ 개념을 사용하는 4가지 방법
개념을 사용하는 4가지 방법
더욱 구체적으로 말하면, 우리는 네 가지 다른 방법을 선택할 수 있다.
내가 공유하고자 하는 모든 방식에 대해 우리는
Number
라는 개념을 가지고 있다고 가정하자.우리는 매우 간단한 실현을 사용할 것이다.다른 코드 세션을 시도하려면 개념을 사용할 수 있지만, 기능적 의미에서 완전하지 않다는 것을 명심하십시오.다음 회에서는 이 점을 상세하게 소개할 것이다.#include <concepts>
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;
사용
requires
자구상기 네 가지 방법 중 첫 번째 방법에서 우리는 템플릿 매개 변수 목록과 함수 반환 유형 사이에
requires
자구를 사용한다. 이 예에서는 auto
이다.template <typename T>
requires Number<T>
auto add(T a, T b) {
return a+b;
}
우리가 이 개념을 어떻게 사용하는지 주의하십시오. 우리는 requires
자구에서 어떤 T
템플릿 매개 변수가 이 개념Number
의 요구를 충족시켜야 하는지를 정의합니다.귀환 유형을 확정하기 위해서 우리는
auto
유형 추정만 사용할 수 있지만 T
유형 추정도 사용할 수 있다.불행히도 우리는 같은 유형의 두 숫자를 더할 수밖에 없었다.우리는
float
와 int
를 더할 수 없다만약 우리가 이렇게 시도한다면, 우리는 좀 길지만 쉽게 이해할 수 있는 잘못된 소식을 얻게 될 것이다.
main.cpp: In function 'int main()':
main.cpp:15:27: error: no matching function for call to 'add(int, float)'
15 | std::cout << add(5,42.1f) << '\n';
| ^
main.cpp:10:6: note: candidate: 'template<class T> requires Number<T> auto add(T, T)'
10 | auto add(T a, T b) {
| ^~~
main.cpp:10:6: note: template argument deduction/substitution failed:
main.cpp:15:27: note: deduced conflicting types for parameter 'T' ('int' and 'float')
15 | std::cout << add(5,42.1f) << '\n';
| ^
여러 종류의 수량을 늘리려면 두 번째 템플릿 파라미터를 도입해야 합니다.template <typename T,
typename U>
requires Number<T> && Number<U>
auto add(T a, U b) {
return a+b;
}
그리고 add(1, 2.14)
이런 전화도 작용한다.개념이 수정되었습니다.단점은 모든 새로운 함수 매개 변수에 대해 새로운 템플릿 매개 변수와 그에 대한 요구를 도입해야 한다는 것이다.리퀘스트 자구를 통해 우리는 더욱 복잡한 제약을 표현할 수 있다.예를 들어'내연'숫자의 정의를 살펴보자.
template <typename T>
requires std::integral<T> || std::floating_point<T>
auto add(T a, T b) {
return a+b;
}
더 나은 가독성을 위해, 대부분의 경우, 나는 개념을 명명하는 것이 더 좋은 방법이라고 생각한다. 특히 당신이 더 복잡한 표현식을 가지고 있을 때.뒤따르다
requires
조항우리는 함수 매개 변수 목록(그리고 한정자-
requires
, const
등과 함수가 실현되기 전에 이른바 트레이닝override
자구를 사용할 수 있다.template <typename T>
auto add(T a, T b) requires Number<T> {
return a+b;
}
우리는 requires
자구와 같은 결과를 얻었고, 우리는 단지 다른 의미로 그것을 썼을 뿐이다.이것은 우리가 여전히 두 가지 다른 유형의 숫자를 추가할 수 없다는 것을 의미한다.템플릿 정의를 이전처럼 수정해야 합니다.template <typename T, typename U>
auto add(T a, U b) requires Number<T> && Number<U> {
return a+b;
}
하지만 우리도 확장성이 있는 단점이 있다.서로 다른 유형에 속할 수 있는 모든 새 함수 매개 변수는 자신의 템플릿 매개 변수를 필요로 한다.requires
자구와 마찬가지로 뒤에 있는 requires
자구에서 더욱 복잡한 제약을 표현할 수 있습니다.template <typename T>
auto add(T a, T b) requires std::integral<T> || std::floating_point<T> {
return a+b;
}
구속 템플릿 매개변수
개념을 사용하는 세 번째 방법은 이전의 방법보다 간결하고 제한이 있다.
template <Number T>
auto add(T a, T b) {
return a+b;
}
보시다시피, 저희는 requires
자문을 필요로 하지 않습니다. 템플릿 파라미터를 설명하는 곳에서 요구 사항을 정의하면 됩니다.우리는 키워드가 아닌 개념명을 사용한다. typename
우리는 앞의 두 가지 방법과 같은 결과를 얻을 것이다.만약 네가 믿지 않는다면, 내가 너에게 가서 살펴보라고 충고한다. Compiler Explorer
이와 함께 이런 방법은 한계가 있다는 점도 주목할 만하다.두 가지 방식 중 어느 하나로든
requires
자구를 사용할 때, 예를 들어 requires std::integral<T> || std::floating_point<T>
표현식을 정의할 수 있다.제약된 템플릿 매개 변수 방식을 사용할 때 이런 표현식을 사용할 수 없습니다.template <std::integral || std::floating_point T>
유효하지 않습니다.따라서 이런 방식을 통해 당신은 단일한 개념만 사용할 수 있지만 앞의 개념과 마찬가지로 더욱 간결한 형식을 사용할 수 있다.
약어 함수 템플릿
오, 깔끔하고 싶으세요?잘했어!
auto add(Number auto a, Number auto b) {
return a+b;
}
약어 함수 템플릿을 선택할 때 템플릿 매개 변수 목록이나 requires
자구가 필요하지 않습니다.매거 함수 매개 변수의 개념을 직접 사용할 수 있다.한 가지 주의해야 할 것이 있고, 더 많은 것을 언급해야 한다.
개념
Number
에 이어 우리는 auto
을 뒤에 두었다.따라서 우리는 Number
가 유형에 대한 구속이지 유형 자체가 아니라는 것을 알 수 있다.상상해 봐, 네가 보기만 한다면auto add(Number a, Number b)
.사용자로서 Number
가 하나의 유형이 아니라 하나의 개념이라는 것을 어떻게 알았습니까?내가 언급하고 싶은 또 다른 것은, 줄임말 함수 템플릿을 따를 때, 매개 변수의 유형을 혼합할 수 있다는 것이다.
int
를 float
에 추가할 수 있습니다.#include <concepts>
#include <iostream>
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;
auto add(Number auto a, Number auto b) {
return a+b;
}
int main() {
std::cout << add(1, 2.5) << '\n';
}
/*
3.5
*/
따라서 약어 함수 템플릿을 사용하면 여러 개의 템플릿 파라미터를 지정하지 않은 상황에서 서로 다른 유형을 사용할 수 있다.이것은 일리가 있다. 왜냐하면 우리는 실제로 어떤 템플릿 매개 변수도 없기 때문이다.이러한 개념을 사용하는 방법의 단점은 제약을 받는 템플릿 파라미터를 사용하는 것처럼 복잡한 표현식으로 우리의 제약을 표현할 수 없다는 것이다.
어떻게 이 네 가지 방식 중에서 선택을 진행합니까?
우리는 방금 네 가지 개념을 사용하는 방법을 보았으니 함께 그것들을 살펴보자.
#include <concepts>
#include <iostream>
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;
template <typename T>
requires Number<T>
auto addRequiresClause(T a, T b) {
return a+b;
}
template <typename T>
auto addTrailingRequiresClause(T a, T b) requires Number<T> {
return a+b;
}
template <Number T>
auto addConstrainedTemplate(T a, T b) {
return a+b;
}
auto addAbbreviatedFunctionTemplate(Number auto a, Number auto b) {
return a+b;
}
int main() {
std::cout << "addRequiresClause(1, 2): " << addRequiresClause(1, 2) << '\n';
// std::cout << "addRequiresClause(1, 2.5): " << addRequiresClause(1, 2.5) << '\n'; // error: no matching function for call to 'addRequiresClause(int, double)'
std::cout << "addTrailingRequiresClause(1, 2): " << addTrailingRequiresClause(1, 2) << '\n';
// std::cout << "addTrailinRequiresClause(1, 2): " << addTrailinRequiresClause(1, 2.5) << '\n'; // error: no matching function for call to 'addTrailinRequiresClause(int, double)'
std::cout << "addConstrainedTemplate(1, 2): " << addConstrainedTemplate(1, 2) << '\n';
// std::cout << "addConstrainedTemplate(1, 2): " << addConstrainedTemplate(1, 2.5) << '\n'; // error: no matching function for call to 'addConstrainedTemplate(int, double)'
std::cout << "addAbbreviatedFunctionTemplate(1, 2): " << addAbbreviatedFunctionTemplate(1, 2) << '\n';
std::cout << "addAbbreviatedFunctionTemplate(1, 2): " << addAbbreviatedFunctionTemplate(1, 2.14) << '\n';
}
우리는 어떤 형식을 사용해야 합니까?여느 때와 마찬가지로 답은 이것이...복잡한 요구가 있다면 표현식을 사용할 수 있도록
requires
자구나 끝부분 requires
자구가 필요합니다.내가 말한 복잡한 수요는 무슨 뜻입니까?하나의 개념만 포함하는 것은 없다!예컨대
std::integral<T> || std::floating_point<T>
.이것은 제약 템플릿 매개 변수나 줄임말 템플릿 함수로 표현할 수 없습니다.만약 당신이 여전히 그것을 사용하고 싶다면, 복잡한 구속 표현식을 자신의 개념에 추출해야 한다.
이것이 바로 우리가 개념을 정의할 때 한 것이다
Number
.다른 한편, 만약 개념이 여러 개의 매개 변수를 사용했다면, 제약 템플릿 매개 변수나 줄임말 템플릿 함수를 사용할 수 없거나, 적어도 나는 잠시 방법을 찾지 못했을 것이다.만약 내가 복잡한 수요가 있다면, 나는 하나의 개념을 정의하고 명명하고 싶지 않다. 나는 앞의 두 가지 옵션 중 어느 것, 즉 with
requires
자구나 withtrainingrequires
자구를 선택할 것이다.만약 내가 간단한 요구가 있다면, 나는 줄임말 함수 템플릿을 사용할 것이다.줄임말 함수 템플릿은 우리가 사용한
add
와 int
호출 float
과 같이 여러 종류의 호출 함수를 동시에 사용할 수 있다는 것을 기억해야 합니다.만약 이것이 문제이고 requires
자구의 지루성을 경멸한다면, 제약을 받는 템플릿 파라미터를 선택하십시오.우리는 우리가 토론한 것이 틀이라는 것을 또 기억해야 한다.모든 조합에 대해 컴파일러는 컴파일러를 할 때 새로운 전문화를 생성합니다.바이너리 크기나 컴파일 시간의 제한으로 템플릿 사용을 피했다면 이 점을 기억하십시오.
결론
오늘 우리는 개념과 함수 파라미터를 어떻게 결합시켜 사용하는지 이미 이해했다.우리는 네 가지 서로 다른 방법을 상세하게 소개했는데 상세한 방법일수록 구속에 있어서 우리에게 더 많은 유연성을 주었고 가장 간결한 방법(약칭 함수 템플릿)은 호출 함수의 유형에 있어서 우리에게 매우 큰 유연성을 주었다.
다음에 우리가 진정으로 자신의 개념을 작성하기 전에 표준 라이브러리에서 어떤 개념을 얻는지 토론할 것이다.
기대해주세요!
만약 당신이 C++ 개념에 대한 세부 사항을 더 알고 싶다면, check out my book on Leanpub!
Reference
이 문제에 관하여(함수에서 C++ 개념을 사용하는 4가지 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/sandordargo/4-ways-to-use-c-concepts-in-functions-1i94텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)