Hyun2and
[Effective C++] 46: 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자 본문
[Effective C++] 46: 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자
혀니앤 2023. 8. 6. 18:53포인트
모든 타입에 대해 암시적 타입 변환을 지원하는 템플릿과 관계가있는 함수를 제공하는 클래스 템플릿을 만들려고 한다면, 클래스 템플릿 안에 프렌드 함수로 정의한다
클래스 템플릿 안에 작성하면 인라인으로 정의되므로 도우미 함수를 따로 생성한다
정리
항목 24 : 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자 와 관련이 있는 항목
✔️ 이전의 예시를 템플릿으로만 바꿔주자
template<typename T>
class Rational
{
public:
Rational (const T& numerator = 0, const T& denominator = 1);
const T numerator() const;
const T ddenominator() const;
};
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs)
{
//...
}
int main()
{
Rational<int> OneHalf(1, 2);
Rational<int> Result = OneHalf * 2; //그런데 이 부분에서 에러가 난다
return 0;
}
operator* 를 사용하는 과정에서 컴파일러가 어떤 함수를 호출해야할지 모르고 있다.
IDE가 제공하는 에러 내용을 보면 일치하는 연산자를 찾지 못한다고 나온다.
문제는 Ratinonal<int> 타입이 아니라 뒤에 있는 int 타입이다.
템플릿 인자 추론 과정에서는 암시적 타입 변환이 고려되지 않기 때문!
+템플릿 인자 추론
컴파일 시에 발견되지 않고, 직접 인스턴스를 생성하는 과정에서 보통의 문제가 발생한다.
정확하게 일치하는 인자를 계속해서 찾다가 아무 것도 없으면 추론에 실패한다.
함수 템플릿에서만 발생하는 문제다
✔️ Friend 를 사용해서 수정해보자
template<typename T>
class Rational
{
public:
Rational(const T& numerator = 0, const T& denominator = 1) {};
//이 부분을 추가
friend
const Rational operator*(const Rational& lhs, const Rational& rhs);
const T numerator() {};
const T ddenominator() {};
};
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs)
{
//...
}
미리 정의한 operator* 에 대해 Rational class 내부에 friend를 넣어줄 수 있다
이렇게 해주면 컴파일러는 어떤 함수를 호출해야하는지 알게된다
그러나 이 경우에는 링크에 실패한다
오류 LNK2019 "class Rational<int> const __cdecl operator*(class Rational<int> const &,class Rational<int> const &)" (??D@YA?BV?$Rational@H@@AEBV0@0@Z)main 함수에서 참조되는 확인할 수 없는 외부 기호 BookStudy C:\Users\ES58\source\repos\BookStudy\BookStudy\EffectiveC++46.obj 1
함수 또는 변수가 선언되었지만 정의되지 않음 : 선언이 헤더 파일에 있지만 일치하는 정의가 구현되지 않은 경우 LNK2019가 발생할 수 있습니다.
이 항목에 따라 링크가 되지 않은 것으로 보인다
friend
const Rational operator*(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());
}
이런 식으로 friend 부분에서의 내부 코드도 추가해줘야 한다 (클래스 함수 내부에 구현을 직접 작성)
✔️ 프렌드 함수는 도우미만 호출하게 만들기
클래스 안에 정의된 함수는 암시적으로 인라인으로 선언된다. (위의 코드처럼)
클래스 정의 내부에 너무 많은 인라인이 들어가면 좋지 않으므로, 외부의 함수 템플릿 도우미를 호출하게 만든다.
최종 형태
template<typename T>
class Rational
{
public:
Rational(const T& numerator = 0, const T& denominator = 1) {};
friend
const Rational operator*(const Rational& lhs, const Rational& rhs)
{
return DoMultiply(lhs, rhs);
}
const T numerator() const { };
const T denominator() const { }
};
template<typename T>
const Rational<T> DoMultiply(const Rational<T>& lhs, const Rational<T>& rhs)
{
return Rational<T>(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());
}
비록 DoMulitply 함수는 혼합형 곱셉 (Rational / int) 의 연산 을 지원하지 않지만, 이 함수를 호출하는 operator* 가 이를 지원하기때문에 구조상 문제가 없다.
템플릿에 대해 모르는게 많다는 것을 다시 느꼈다..
프렌드 함수도 마찬가지
이 부분은 나중에 다시 읽어봐야 할 것 같다
'공부엔 끝이없다 > Effective C++' 카테고리의 다른 글
[Effective C++] 45: "호환되는 모든 타입" 을 받아들이는 데는 멤버 함수 템플릿이 직방! (0) | 2023.07.31 |
---|---|
[Effective C++] 44: 매개변수에 독립적인 코드는 템플릿으로부터 분리시키자 (0) | 2023.07.31 |
[Effective C++] 43: 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아두자 (1) | 2023.07.23 |
[Effective C++] 1. C++ (0) | 2023.02.19 |
[Effective C++] 0. 서론 (0) | 2023.02.19 |