1

Am getting compilation errors for the following snippet of my C++ code.

struct Power{
  int power;
  int age;
  int operator-(const Power& p1)
  {
     return this->power - p1.power;
  }
};


int main() {
  Power p1;
  p1.power = 1;
  p1.age = 25;
  Power p2;
  p2.power = 2;
  p2.age = 26;
  std::cout<<std::minus<Power>()(p1, p2)<<std::endl;
}

build with c++11. cannot be built. error messages are:

 error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘Power’)
  std::cout<<std::minus<Power>()(p1, p2)<<std::endl;
           ^
In file included from /usr/include/c++/5/iostream:39:0,
                 from rvaluereference.cpp:1:
/usr/include/c++/5/ostream:628:5: note: candidate: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = Power] <near match>
     operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
     ^
/usr/include/c++/5/ostream:628:5: note:   conversion of argument 1 would be ill-formed:
rvaluereference.cpp:60:39: error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
.....
TechEnthusiast
  • 1,795
  • 2
  • 17
  • 32
Wubin Ouyang
  • 777
  • 1
  • 7
  • 9
  • You make it sound like it used to work before overloading the operator. Outputting `p1 - p2` directly doesn't work either. This isn't related to `std::minus`. – chris Oct 17 '17 at 04:07
  • I am sure p1 - p2 works with c++ 11 – Wubin Ouyang Oct 17 '17 at 04:08
  • Oh, that's my bad. I didn't see it returns `int` instead of `Power` like subtraction usually does. – chris Oct 17 '17 at 04:10

2 Answers2

3

std::minus has a single template parameter that defines the type of both inputs and the output; it doesn't handle type switching AFAICT. Your operator- takes Power, and returns int, but std::minus<Power> must take and return Power. The complaint is because std::minus<Power> returns Power, and ostream doesn't have an overload for operator<< that accepts Power.

As noted in the comments, if you can use C++14, std::minus<void> accepts mismatched arguments and deduces the return type, so if you can use C++14, that's another option.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Thanks. And after I changed it to std::cout<<(std::minus()(p1, p2)).age< – Wubin Ouyang Oct 17 '17 at 04:14
  • @WubinOuyang: "does not work" is not particularly helpful; *how* is it failing after the change? – ShadowRanger Oct 17 '17 at 04:17
  • /usr/include/c++/5/bits/stl_function.h:182:20: error: no match for ‘operator-’ (operand types are ‘const Power’ and ‘const Power’) { return __x - __y; } – Wubin Ouyang Oct 17 '17 at 04:17
  • @WubinOuyang: You didn't make `operator-` `const`; `std::minus` accepts its arguments as `const`, and uses them as such. When `operator-` is a member function that isn't declared as `const`, it assumes it might modify the left hand operand (`this`), so it can't be used with a `const` left-hand operand. Either make `operator-` `const`, or more correctly, make it a non-member function, per [the guidelines on when to use member vs. non-member functions](https://stackoverflow.com/a/4421729/364696). – ShadowRanger Oct 17 '17 at 04:30
  • 1
    @WubinOuyang: ...And someday you'll have to deal with the issue that your `int` can't be turned into a `Power` for `minus` to return. C++14 has [minus](http://en.cppreference.com/w/cpp/utility/functional/minus_void), if you can use that... – Davis Herring Oct 17 '17 at 04:36
  • @DavisHerring: They already did that. That's what my answer was addressing, and fixing it uncovered the other problem (lack of `const`-correctness). – ShadowRanger Oct 17 '17 at 04:36
  • @ShadowRanger: I don't think they'll want to leave it as `{return p1;}`, though. – Davis Herring Oct 17 '17 at 04:38
  • @DavisHerring: True. My assumption is they were trying to make a minimal change to make it compile, then fix the logic later. – ShadowRanger Oct 17 '17 at 04:38
-1

While std::minus only can do T operator-(const T &lhs, const T &rhs), your operators do not have the same limitations. Declare the operator outside of the class:

int operator-(const Power &lhs, const Power &rhs) {
    return lhs.power - rhs.power;
}

and now you can do std::cout << p1 - p2 << std::endl;

N00byEdge
  • 1,106
  • 7
  • 18
  • The OP explicitly wants to use `std::minus`. Saying "don't use `std::minus`" is kind of beside the point. Beyond that, while non-member functions are more correct for `operator-` (since it's non-mutating), `std::minus` should work just fine with member functions if they follow the required pattern (function is `const`, argument is `const` reference of same type as the class it was defined on, and result is non-`const` value of same type). – ShadowRanger Oct 17 '17 at 04:33
  • @ShadowRanger Yes but here `operator-` isn't returning a `Power`, is it? Therefore `std::minus` is not valid for this usage. – N00byEdge Oct 17 '17 at 04:45