Item 24: 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자

일반적으로 클래스의 암시적 타입변환은 좋지 않은 생각이다. 그런데 예외중 하나가 숫자 타입을 만들 때이다. 예를 들어 유리수를 나타내는 클래스가 있다면 int에서 이 클래스로의 암시적 변환은 나쁘지 않은 생각일 것이다.

1. 유리수를 나타내는 클래스를 만들어 보자
class Rational {
public:
    Rational(int numerator = 0,    // int에서 Rational으로 암시적
             int denominator = 1); // 변환을 하기 위해 explicit 안붙임

    int numerator() const;
    int denominator() const;

private:
    ...
};
2. 곱하기 기능을 하는 operator* 함수를 작성하자.
class Rational {
public:
    ...
    const Rational operator*(const Rational& lhs) const;
};
3. 위 코드로 가능한 연산은?
Rational oneEight(1, 8);
Rational oneHalf(1, 2);

Ratonal result = oneEight * oneHalf;   // OK
result = result * oneEight;            // OK
4. 아래 코드는 가능한가?
result = oneHalf * 2; // ----> 1번
result = 2 * oneHalf; // ----> 2번

1번은 가능하고 2번은 불가능(컴파일 에러)

5. 2번이 불가능한 이유는?

1번은 아래와 같은 코드이다.

const Rational temp(2);                  // 암시적 변환

result = oneHalf.operator*(temp); // 암시적 변환

컴파일러는 Rational의 생성자가 비명시적으로 선언되어 있기 때문에 2를 이용해서 Rational을 만들 수 있다. 더구나 파라미터가 디폴트 값을 가지고 있다. 그래서 2에서 Rational 객체를 만들 수 있다.

2번의 경우에 컴파일러는 아래와 같이 시도하려고 하나

result = 2.operator*(oneHalf);

2 번의 경우 2는 클래스와 연관되어 있지 않다. operator* 함수를 호출하려고 하나 있지도 않다. 컴파일러는 계속해서 네임스페이스 혹은 전역 영역에서 operator*(2, oneHalf) 와 같이 호출할 수 있는 함수가 있는지 찾아 보나 존재할리 없다.

암시적 타입 변환에 매개변수가 먹혀들려면 매개변수리스트에 들어 있어야만 한다.

호출되는 멤버 함수를 갖고 있는(this가 가리키는) 객체에 해당하는 암시적 매개변수에는 암시적 변환이 먹히지 않는다.

이 말은 다음과 같다.

1번의 경우는 Rational::operator*(const Ration& lhs) 함수의 매개 변수로 암시적 변환이 가능하다.

2번의 경우 2를 Ratonal객체로 변경 후, 이 객체를 이용하여 operator* 함수를 호출하도록 암시적 변환이 일어나지 않는다. 즉 이런 암시적 변환은 지원하지 않는다는 뜻이다.

6. 만약 Rational의 생성자가 explicit으로 선언되면 위 두 방식은 지원되는가?

둘다 안된다. 암시적 변환이 되지 않기 때문이다. 그래도 최소한 두 방식이 지원되지 않는다는 일관성은 있게된다.

7. 두 방식 다 지원되게 하려면 어찌해야 하나?
class Rational {
    ...           // 이제 operator* 함수가 없다
};

const Rational operator*(const Rational& lhs, // 이제는 비멤버
                         const Rational& rhs) // 함수이다
{
    return Rational(lhs.numerator() * rhs.numerator(),
                    lhs.denominator() * rhs.denominator());
}

Rational oneFourth(1, 4);
Rational result;

result = oneFourth * 2;        // OK
result = 2 * oneFourth;        // 이것도 OK
8. 이경우에 위 함수를 프렌드로 선언해도 될까?

이 경우에는 아니다. 왜냐하면 Rational의 public 함수들만을 이용해서 구현이 가능하기 때문이다.

프렌드 함수는 가능한 피하자.

정리

  • 어떤 함수에 들어가는 모든 매개변수(this포인터가 가리키는 객체도 포함해서)에 대해 타입 변환을 해 줄 필요가 있다면, 그 함수는 비멤버이어야 한다.

results matching ""

    No results matching ""