6

I have a struct template A<x> and a + operator with int.

#include <iostream>
template<int x>
struct A{
    int a;  
};
template<int x>
int operator+(A<x> a, int b){
    return a.a+b;
}

I created a struct template B<x>, which is convertible to A<x>.

template<int x>
struct B{
    int b=3;
    operator A<x>(){
        return {b+10};
    }
};

Now I want B<x> to be converted to A<x> when calling B<x> + int.

int main(){
    std::cout<<(A<12>{9}+10)<<std::endl;//OK
    std::cout<<(B<12>{9}+10)<<std::endl;//Error
    return 0;
}

I read Implicit conversion when overloading operators for template classes and wrote

template<int x>
struct B{
    int b=3;
    operator A<x>(){
        return {b+10};
    }
    friend int operator+(A<x> a, int b);
};

, but it didn't work because the declared friend int operator+(A<x> a, int b) does not match template<int x> int operator+(A<x> a, int b).

I read C++ - How to declare a function template friend for a class template and made friend declaration template, but it didn't work because the template parameter couldn't be deduced.

Of course I could write operator+ for both A and B, but I have dozens of operators and I don't want to do it.

What is the correct way to do this?

eivour
  • 1,678
  • 12
  • 20

4 Answers4

5

Looking at the two ways to make a non-member operator+ for A, we can either make it a function template:

template <int x>
int operator+(A<x>, int);

which won't match B<x> because we're just doing template deduction, which doesn't allow for conversions.

Or, we can make it a friend non-template:

template <int x>
struct A {
    friend int operator+(A a, int );
};

which also won't match B<x> because name lookup won't consider that function. Unless, that is, we tell it to:

template <int x>
struct B {
    friend int operator+(A<x>, int ); // NB: not a template
};

Now, our original non-template operator+ will be considered, the conversion will be performed as desired, and your code prints 29.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • But then I would not be able to add an A and an int. – eivour Aug 18 '17 at 03:55
  • @eivour Don't understand. Why do you think not? – Barry Aug 18 '17 at 12:58
  • @Barry I think he means that the binary `operator+(...)` previously defined outside both classes is no longer seen by `A`. Thus, you are correct that the code prints `29`, however the first line that prints `19` throws an error. From your answer this is not clear. – pingul Aug 18 '17 at 21:45
  • @pingul Huh? No, both work if you defined the `A` operator within the class. – Barry Aug 19 '17 at 04:10
  • @Barry you mean I should define(not declare) operator+ in both B and A? – eivour Aug 19 '17 at 08:51
  • @Barry Maybe you could provide a runnable example? I've tried to interpret your answer, but I can't reproduce the result. – pingul Aug 19 '17 at 11:25
  • @Barry Oh I see; I had misinterpreted what you meant with making the function a _"friend non-template"_. I added a fully working example to your answer. Feel free to not accept it in case you don't like it, but it was necessary for me to understand your answer. – pingul Aug 19 '17 at 13:26
1

You maybe have seen this already, but at least it is still possible to do an explicit cast, and it might be useful:

int main(){
    std::cout<<(A<12>{9}+10)<<std::endl;                     // prints 19
    std::cout<<(static_cast<A<12>>(B<12>{9})+10)<<std::endl; // prints 29
    return 0;
}
pingul
  • 3,351
  • 3
  • 25
  • 43
1

I tried to edit Barrys answer with the following (runnable) code, that produces the correct output, but it was rejected there.

I'll add it here in case anyone else is curious.

#include <iostream>

template <int x>
struct A {
    int a;
    friend int operator+(A a, int b) { return a.a + b; }
};

template <int x>
struct B {
    int b;
    operator A<x>() { return {b+10}; }
    friend int operator+(A<x>, int );
};

int main() {
    std::cout << (A<12>{9} + 10) << std::endl;
    std::cout << (B<12>{9} + 10) << std::endl;
}

Which prints

19
29
pingul
  • 3,351
  • 3
  • 25
  • 43
  • Why is there an error, when I change friend int operator+(A, int ); in struct B to something like friend int operator+(A<12>, int );? Shouldn't it lead to the same result? – Phil Apr 13 '21 at 10:34
0

I hope it may help you.

I have templated friend operator+

Derived from A<x> and it compiled, but after calling friend operator+ variable a was uninit so i got immediate values. You have to set a somehow and this seems to work.

#include <iostream>

template<int x>
struct A
{
    int a{x};
};

template<int x>
int operator+(A<x> a, int b)
{
    return a.a+b;
}

template<int x>
struct B : A<x>
{
    int b;

template<int U>
    friend int operator+(A<U> a, int b);
};

int main(void)
{
    std::cout<< (A<20>{}+10)<<std::endl; // prints 30
    std::cout<< (B<10>{}+10)<<std::endl; // prints 20
    return 0;
}
kocica
  • 6,412
  • 2
  • 14
  • 35
  • It compiles, but it changes the result. I want to add 10 automatically when converting from B to A. I also don't want users to change the value of a when using as B. – eivour Aug 17 '17 at 12:25
  • @eivour It doesnt change any instance of `A`. Ill take a look how could we add `10` when converting. – kocica Aug 17 '17 at 12:29