2

I'm learning C++, and I'm a little unclear on how inheritance and operator overloading work, so I might very well be doing something silly here.

I have a base class that defines some very basic operations for representing units of measurement:

#pragma once
class UnitOfMeasure
{
public:
    UnitOfMeasure(void) : mAmount(0) {}
    UnitOfMeasure(double amount) : mAmount(amount) { }
    ~UnitOfMeasure() {}

    void SetAmount(double amount) { mAmount = amount; }

    UnitOfMeasure& operator+=(const UnitOfMeasure& rhs)
    {
        mAmount += rhs.mAmount;
        return *this;
    }

    friend bool operator==(const UnitOfMeasure&, const UnitOfMeasure&);

protected:
    double mAmount;
};

bool operator==(const UnitOfMeasure& lhs, const UnitOfMeasure &rhs)
{
    return rhs.mAmount == lhs.mAmount;
}

Subclasses then implement specific conversions like this:

#pragma once
#include "UnitOfMeasure.h"

class Temperature : public UnitOfMeasure
{
public:
    enum TemperatureUnit { CELSIUS, FAHRENHEIT };
    Temperature(void) { }
    Temperature(double amount, TemperatureUnit units=CELSIUS) { SetAmount(amount, units); }
    ~Temperature(void) {};
    void SetAmount(double amount, TemperatureUnit units=CELSIUS)
    {
        switch(units)
        {
            case CELSIUS: { mAmount = amount; break; }
            case FAHRENHEIT: { mAmount = (amount - 32) / 1.8; break; }
        }
    }
    double Fahrenheit() { return 32 + (mAmount * 1.8); }
    double Celsius() { return mAmount; };
};

In my sample program, I'm storing instances of Temperature in a list, and this is where things start to get weird. When all the code is contained in the .h file, everything is just fine. I can compile and run successfully. However, the compiler complains when I break Temperature's code out into a separate .cpp file. I get these messages:

1>  Temperature.cpp
1>Temperature.obj : error LNK2005: "bool __cdecl operator==(class UnitOfMeasure const &,class UnitOfMeasure const &)" (??8@YA_NABVUnitOfMeasure@@0@Z) already defined in BadComparison.obj
1> BadComparison.exe : fatal error LNK1169: one or more multiply defined symbols found

(I'm using Visual Studio 2012)

Is the compiler creating a separate == operator for my Temperature class?

Thanks!

Alex
  • 93
  • 2
  • 12
  • What you're doing is not going to work how you want. Namely, your code thinks that 100 celcius equals 100 ferenheight equals 100 inches. :( `boost::units` – Mooing Duck Mar 19 '13 at 23:52
  • possible duplicate of [Friend operator overloading causes "already defined in" linker errors](http://stackoverflow.com/questions/9274753/friend-operator-overloading-causes-already-defined-in-linker-errors) – joce Mar 28 '13 at 03:06

1 Answers1

6

You should declare your operator as inline to avoid its definition being part of multiple translation units that #include the corresponding header file:

    inline bool operator==(const UnitOfMeasure& lhs, const UnitOfMeasure &rhs)
//  ^^^^^^
    {
        return rhs.mAmount == lhs.mAmount;
    }

When that is the case, the linker will eventually complain that the same function is being defined multiple times in your program, and that is a violation of the One Definition Rule.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • That seems to have done the trick, thank you. But I'm still at a loss as to why the compiler thought there were multiple definitions of that operator. – Alex Mar 20 '13 at 01:03
  • Without the inline, every .cpp file that #included the header effectively defined that function again. – Roger Rowland Mar 20 '13 at 06:01
  • @Alex: The compiler processes each translation unit (i.e. `.cpp` file) separately, and when processing a translation unit it won't remember what was processed while compiling *other* translation units. If both `a.cpp` and `b.cpp` include `c.h`, and `c.h` contains a function *definition*, this definition will be compiled both in the object code produced from `a.cpp` (say `a.o`) and in the object code produced from `b.cpp` (say `b.o`). Then, when the linker will merge `a.o` and `b.o` to obtain your `.exe`, it will find a function defined multiple times, and issue an error. – Andy Prowl Mar 20 '13 at 13:18
  • @Alex: The `inline` keyword prevents this from happening, and allows you to put the definition of a function in a header (notice, that regular programming practice is to have function definitions in `.cpp` files, unless they are templates or member functions of a class template). – Andy Prowl Mar 20 '13 at 13:19
  • Yes, this helped. Thank you. I think I get it now. I thought that #pragma once (or #ifndef/#define/#end) was the safeguard against that sort of thing, but that's apparently not the case due to the link step. – Alex Mar 20 '13 at 17:16
  • @Alex: Right. [This Q&A](http://stackoverflow.com/questions/14909997/why-arent-my-include-guards-preventing-recursive-inclusion-and-multiple-symbol) may help understanding why in more detail (the second part of the question/answer in particular) – Andy Prowl Mar 20 '13 at 17:17