3
\$\begingroup\$

I'm writing some code that helps me deal with units better, by having a user defined type for things like Metres, Centimetres and Millimetres. Here is the class declaration for Metres:

template <typename StorageType>
class Metres : public Unit<StorageType>
{
public:
    static Metres fromMetres (StorageType metres) noexcept;

    Metres operator+ (Metres rhs) const noexcept;
    Metres operator+ (Centimetres<StorageType> rhs) const noexcept;
    Metres operator+ (Millimetres<StorageType> rhs) const noexcept;

protected:
    using Unit<StorageType>::Unit;
};

However, when writing the definitions of the operator+ functions, I find myself repeating Metres<StorageType> quite a lot:

template <typename StorageType>
Metres<StorageType> Metres<StorageType>::fromMetres (StorageType metres) noexcept
{ return { metres }; }

template <typename StorageType>
Metres<StorageType> Metres<StorageType>::operator+ (Millimetres<StorageType> rhs) const noexcept
{ return { this->getRawValue() + rhs.getRawValue() / StorageType (1000) }; }

template <typename StorageType>
Metres<StorageType> Metres<StorageType>::operator+ (Centimetres<StorageType> rhs) const noexcept
{ return { this->getRawValue() + rhs.getRawValue() / StorageType (100) }; }

template <typename StorageType>
Metres<StorageType> Metres<StorageType>::operator+ (Metres<StorageType> rhs) const noexcept
{ return { this->getRawValue() + rhs.getRawValue() }; }

I can't define the functions in the class declaration, as the types Centimetres and Millimetres forward declared. If I define these classes beforehand, I end up with the same problem in a different class.

I feel like this could be done better. How can I reduce the amount of visual noise in these function definitions?


Note: If there are any rules in C++ that allow the omission of <StorageType> in certain places, quotes from the standard are much appreciated.

\$\endgroup\$
1
\$\begingroup\$

The obvious possibility would be to just define the functions inside the class definition:

template <typename StorageType>
class Metres : public Unit<StorageType> {
public:
    static Metres fromMetres(StorageType metres) noexcept { return metres; }

    Metres operator+ (Metres rhs) const noexcept { 
        return getRawValue() + rhs.getRawValue(); 
    }

    Metres operator+ (Centimetres<StorageType> rhs) const noexcept { 
        getRawValue() + rhs.getRawValue() / StorageType(100); 
    }

    Metres operator+ (Millimetres<StorageType> rhs) const noexcept { 
        getRawValue() + rhs.getRawValue() / StorageType(1000); 
    }

protected:
    using Unit<StorageType>::Unit;
};

The problem (as I'd see it, anyway) stemmed from two facts: these are templates, so you needed to include the template parameters, and the bodies of the functions in question are so tiny, they basically got lost in the noise.

Both of these point to the fact that (in this case) including the function definitions inline in the class definition gains a great deal, and loses nothing.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the suggestion, however I can't do this because Centimetres and millimetres hasn't been declare at this point (only forward declared), and if I do declare them first I end up with this same issue in another place. I'll edit my question to make this clearer. \$\endgroup\$ – OMGtechy Oct 30 '15 at 23:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.