1

I have included the minimal amount of code to replicate this issue.

I would like it if I could assign a Furlong object to a Metric object and vice versa but instead the compiler gives me this error:

no operator "=" matches these operands

Header file:

#ifndef DISTANCE_H_
#define DISTANCE_H_

using namespace std;
#include <iostream>

//const to facilitate conversions between the two classes

const double FEET_IN_METERS = 0.3048;
const double FURLONG_TO_FEET = 660;

class Furlong;
class Metric;

class Furlong {
public:

    Furlong(int fur = 0, int yar = 0, int fee = 0.0);

    // Copy Constructor.

    Furlong( const Furlong& rhs );

    // Destructor.

    ~Furlong(void);

    // Assignment operator=.
    Furlong&    operator=( Furlong& rhs);

    int furlong;
    int yards;
    double feet;

};

class Metric {

public:
Metric(int kilo = 0, double mete = 0.0);

Metric (const Metric& rhs);

~Metric(void);

Metric& operator=(Metric& rhs);

Metric (Furlong& f);

operator Furlong();

int kilometers;
double meters;
};

#endif

Class Definition file:

#include <stdlib.h>
#include "Distances.h"

//FURLONG CLASS DEFINITIONS

//Constructor

Furlong::Furlong(int fur, int yar, int fee) {
    furlong = fur;
    yards = yar;
    feet = fee;
}

// Copy Constructor.

Furlong::Furlong( const Furlong& rhs ) {
    furlong = rhs.furlong;
    yards = rhs.yards;
    feet = rhs.feet;
}

    // Destructor.

Furlong::~Furlong(void) {

}

    // Assignment operator=.

Furlong& Furlong::operator=( Furlong& rhs) {

    furlong = rhs.furlong;
    yards = rhs.yards;
    feet = rhs.feet;

    return *this;

}
//METRIC CLASS DEFINITONS


Metric::Metric(int kilo, double mete) {
    kilometers = kilo;
    meters = mete;
}
Metric::Metric(const Metric& rhs) {

    kilometers = rhs.kilometers;
    meters     = rhs. meters;
}

Metric::~Metric(void) {

}

Metric& Metric::operator=(Metric& rhs) {
    kilometers = rhs.kilometers;
    meters     = rhs.meters;

    return *this;
}

// conversion constructor

Metric::Metric (Furlong& f) {
 kilometers = 3;
 meters = 2.0;
}

//conversion operator

Metric::operator Furlong() {
    return Furlong(1, 2, 3.0);
}

Main file:

#include <stdlib.h>
#include "Distances.h"


using namespace std;

int main() {
    Furlong one(1,2,3.0); 
    Furlong three(4,5,6.0);
    Metric two(7,8.0);
    Metric four(9, 10.0);

    one = two;
    four = three;

    return 0;
}

I would like object two to be converted to type Furlong then assigned to object one. In addition, object three should be converted to type Metric then assigned to object four

Donald Duck
  • 8,409
  • 22
  • 75
  • 99

2 Answers2

1

You simply need to make the conversion explicit for the compiler to find the path:

one =  (Furlong) two;
four = (Metric) three;
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
1

The problem, believe it or not, is merely a lack of const correctness.

class Furlong
{
public:
    // ...

    // Assignment operator=.
    Furlong&    operator=( Furlong& rhs);

    // ...    
};

class Metric 
{

public:
    // ...

    Metric& operator=(Metric& rhs);

    // ...
};

When you attempt to assign a Furlong to a Metric, here's what happens:

  1. There is no Metric::operator=() which takes a Furlong, or which takes a [cv] Furlong&.
  2. The compiler determines whether an implicit conversion is possible. In this case, you provided constructor Metric (Furlong& f), which can be used.
  3. The compiler calls this constructor, creating a temporary Metric, which is then used to call Metric& Metric::operator=(Metric&).
  4. Since Metric::operator=()'s parameter is non-const, and doesn't take an rvalue reference, it cannot bind to temporary objects. As no other potential conversions are available, the compiler thus emits an error.

Similarly, when attempting to assign a Metric to a Furlong, Metric::operator Furlong() is used to create a temporary Furlong, but Furlong::operator=() is unable to accept temporaries as arguments, making conversion impossible.


Thus, the solution is simple: Since neither operator=() modifies rhs, we take it by const reference.

class Furlong
{
public:
    // ...

    // Assignment operator=.
    Furlong&    operator=(const Furlong& rhs);

    // ...    
};

class Metric 
{

public:
    // ...

    Metric& operator=(const Metric& rhs);

    // ...
};

Furlong& Furlong::operator=(const Furlong& rhs)
{
    // ...
}

Metric& Metric::operator=(const Metric& rhs)
{
    // ...
}

This allows rhs to bind to temporary objects, such as the Furlong generated by Metric::operator Furlong() and the Metric generated by Metric(Furlong&). The code will then compile successfully.

  • This isn't directly related, but I would also suggest: 1) **_NEVER_** put a `using` directive (and especially not `using namespace std;`) in a header, because it can inadvertently break any code that uses that header. If any function defined in the header need members of the standard namespace, either fully qualify the members' names, or put your `using` inside that function. (It's safer to put `using` directives in source files, but that can still cause trouble because of just how many common names are used by the standard library.) – Justin Time - Reinstate Monica May 05 '17 at 16:59
  • 1
    2) If you only need standard library I/O type declarations in the header (such as when you have pointers/references to those types), use [``](http://en.cppreference.com/w/cpp/header/iosfwd) instead of ``; it merely declares the types, and typically results in faster compilation times. If you need to use the standard output objects themselves in the header (like `cout`), or need to actually _define_ variables of any of the I/O types (like an `fstream`), then you still need to use the appropriate header (e.g., `` for `cout`, or `` for `fstream`). – Justin Time - Reinstate Monica May 05 '17 at 17:05
  • 3) `Metric (Furlong& f)` should take its parameter by `const` reference, both to indicate that it won't be modified, and to allow it to bind to temporaries. If you change this, remember to change both the declaration and the definition. – Justin Time - Reinstate Monica May 05 '17 at 17:08
  • 4) `Metric::operator Furlong()` should be marked as `const`, since it doesn't modify the `Metric`: `operator Furlong() const`. Again, if you change this, remember to change both the declaration and the definition. – Justin Time - Reinstate Monica May 05 '17 at 17:10
  • 5) Since you're manually defining copy constructors, you might also want to look into move constructors (`Furlong(Furlong&&)` and `Metric(Metric&&)`) and move assignment operators (`Furlong& Furlong::operator=(Furlong&&)` and `Metric& Metric::operator=(Metric&&)`), and into "smart" assignment operators (`Furlong& Furlong::operator=(Furlong)` and `Metric& Metric::operator=(Metric)`, which take their parameter by value, using either the copy constructor or move constructor to create it depending on the situation). – Justin Time - Reinstate Monica May 05 '17 at 17:18
  • See [here](http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) for more information about why not to use `using namespace std;` in a header, and [here](http://stackoverflow.com/a/3109981/5386374) for more information about move semantics. – Justin Time - Reinstate Monica May 05 '17 at 17:20