Hyun2and

[Effective C++] 46: 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자 본문

공부엔 끝이없다/Effective C++

[Effective C++] 46: 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의해 두자

혀니앤 2023. 8. 6. 18:53
728x90

포인트

모든 타입에 대해 암시적 타입 변환을 지원하는 템플릿과 관계가있는 함수를 제공하는 클래스 템플릿을 만들려고 한다면, 클래스 템플릿 안에 프렌드 함수로 정의한다
클래스 템플릿 안에 작성하면 인라인으로 정의되므로 도우미 함수를 따로 생성한다

 


정리

항목 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 타입이다.

템플릿 인자 추론 과정에서는 암시적 타입 변환이 고려되지 않기 때문!

 

 

+템플릿 인자 추론

더보기

https://wikidocs.net/408

https://wikidocs.net/476

컴파일 시에 발견되지 않고, 직접 인스턴스를 생성하는 과정에서 보통의 문제가 발생한다.

정확하게 일치하는 인자를 계속해서 찾다가 아무 것도 없으면 추론에 실패한다.

함수 템플릿에서만 발생하는 문제다

 

 


✔️ 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

https://learn.microsoft.com/ko-kr/cpp/error-messages/tool-errors/linker-tools-error-lnk2019?view=msvc-170&f1url=%3FappId%3DDev16IDEF1%26l%3DKO-KR%26k%3Dk(LNK2019)%26rd%3Dtrue 

 

함수 또는 변수가 선언되었지만 정의되지 않음 : 선언이 헤더 파일에 있지만 일치하는 정의가 구현되지 않은 경우 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* 가 이를 지원하기때문에 구조상 문제가 없다.


 

 

 

템플릿에 대해 모르는게 많다는 것을 다시 느꼈다..

프렌드 함수도 마찬가지

 

이 부분은 나중에 다시 읽어봐야 할 것 같다

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90
Comments