2
template <class T>
class A {

    struct B {
         T a,b;
    }

    B& operator+ (B & x, B & y) {
        return B(x.a + y.a, x.b + y.b);
    }    

    int funcA (B & x, B & y){
        return (x + y).a;
    }
};

As you might guess, during compilation I get "operator+ must take either zero or one argument". Right. Because in the operator+ the "this" is passed as first argument. So, the a solution is to locate the operator outside of the class A definition. However A's function funcA uses operator+. So it has to be defined before A. But operator+ itself uses class B defined in the A which is a template itself and B is dependent class.

What's the solution?

user unknown
  • 35,537
  • 11
  • 75
  • 121
OlegG
  • 975
  • 3
  • 10
  • 30
  • @user315052: While *forward declaration*s are the general answer, in this case it is not, rather than moving the declaration of the operator forward (which cannot be done here) the definition of `funcA` must be moved downwards. – David Rodríguez - dribeas Jun 25 '12 at 12:26
  • Downwards means where? It is necessary by definition to have funcA as member of A. – OlegG Jun 25 '12 at 12:32
  • +1 on question, because I liked Steve Jessop's answer so much. – jxh Jun 25 '12 at 16:21
  • @DavidRodríguez-dribeas: My intention was to achieve the effect of Steve Jessop's answer, but have `operator+` forward declared above of `A`, and implemented below `A`. I don't understand why an inlined `friend` can let `funcA` use `(x + y)`, but an "outlined" `friend` has to be called explicitly. – jxh Jun 25 '12 at 16:47
  • @user315052: `B` is a nested type (`A::B`), so it cannot be forward declared *before* the definition of the enclosing type `A`. That in turn means that `operator+(A::B,A::B)` cannot be declared at namespace level before the definition of `A`, so it cannot be declared as a non-friend before. Now, if you make it a `friend`, then you can both provide the definition *inlined* inside `B` or *out of line* after the class definition (the definition does not affect lookup), and both options would be fine. But that is not a **forward** declaration, rather a **friend** declaration. – David Rodríguez - dribeas Jun 25 '12 at 17:03
  • @DavidRodríguez-dribeas: I did forward declare it. See my posted answer. – jxh Jun 25 '12 at 17:15
  • @user315052: Uhm... it is a template and that makes a difference that I missed. When processing templates, dependent names are resolved at the point of instantiation, which in this case (as the call is nested inside a function of a different template) it is the point of instantiation of `A<>::A()` which in turn is late enough. Note that the semantics are still different than in Steve's answer: he befriends (and defines) a single *non-templated* `operator+` function, while in your proposed solution you befriend *all* instantiations of a *templated* `operator+`. – David Rodríguez - dribeas Jun 25 '12 at 17:42
  • @DavidRodríguez-dribeas: If the desire is to implement the `operator+` outside of `A`, it has to be a template, because `A::B` is a different type from `A::B`. – jxh Jun 25 '12 at 17:56
  • @user315052: The question is what is more important for the user, being able to declare it outside of `A` (and thus make it a template) or prefer the leaner solution of (ab)using `friend`. My guess is that the user only wants the code to compile and probably does not even know the difference. – David Rodríguez - dribeas Jun 25 '12 at 18:35
  • @DavidRodríguez-dribeas: The leanest solution to the example code is molbdnilo's solution. If the operator needs access to `A` though, then Steve Jessop's solution will work if it is moved out of `B` into `A`. Sometimes when the code example is simplified to illustrate the problem, the simplification loses the complexity that justified needing to solve it. – jxh Jun 25 '12 at 19:46

3 Answers3

3

There is a way to define a free function inside a class's body:

struct B {
    T a,b;
    // the change from B& to B is nothing to do with defining
    // the function here, it's to avoid returning a dangling reference.
    friend B operator+ (B & x, B & y) {
        return B(x.a + y.a, x.b + y.b);
    }
};

Seems to me that this is the simplest way to deal with this case.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Why frined if function is inside of class and moreover class is stgruct and all memebers are public accessible? And second thing does it prevent us from passing "this" to the operator+ as first argument? – OlegG Jun 25 '12 at 11:54
  • It's a free function, not a member function. So no, you can't use `this`. The reason I have made it a struct with all members public, is that you did the same for the class `B` in your code. – Steve Jessop Jun 25 '12 at 12:58
  • It needs to return a value, not a reference to a temporary; and it would be more polite to take the arguments by value or `const` reference. – Mike Seymour Jun 25 '12 at 13:31
  • @Mike: true, I just copied the questioner's definition of the function in order to demonstrate the technique. – Steve Jessop Jun 25 '12 at 13:45
  • @SteveJessop: It also works if you declare it a free function within `A` after `B`'s definition. Also, +1 from me. – jxh Jun 25 '12 at 16:23
  • @user315052: What you are suggesting has other issues, in particular with friendship and granting unwarranted access to other code. In particular it can be easily exploited to gain access to any of the private members of `A` – David Rodríguez - dribeas Jun 25 '12 at 17:18
  • @DavidRodríguez-dribeas: I only suggested it because it would be the same scope that the OP had placed the definition. If the OP's intention is to only allow `B`'s within `A` to be added, then I don't understand the hesitation to define a regular `operator+` method for `B` as molbdnilo suggested. – jxh Jun 25 '12 at 17:37
0

In addition to @SteveJessop's answer - which is the best answer - if the operator is to be a member, it has to be a member of B, not of A:

template <typename T>
class A {
public:
    struct B {
       T a,b;
       B(const T& x, const T& y) : a(x), b(y) {}
       B operator+(const B& rhs) const { return B(a + rhs.a, b + rhs.b); }
    };

    T funcA (B & x, B & y){
        return (x + y).a;
    }
};
molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • As for me, it is much more elegant solution than with `friend` one. And much more natural. But is it possible in case, say, `operator* (int a, B & x)` and `operator* (B & x, int a)` ? Seems, we have to use `friend` here. – OlegG Jun 26 '12 at 06:44
  • @Oleg: Yes, you have to do that. The most popular way to implement operators is to do the "assignment-like" operators (`+=`, `*=`, etc.) as members, then do the corresponding binary operators (`+`, `*`,...) as free functions in terms of the assignment operators. – molbdnilo Jun 26 '12 at 13:37
0

You can forward declare operator+ to be outside A, but funcA has to call it explicitly. For this case, you probably do not want to define operator+ outside of A, but since you had asked

So, the a solution is to locate the operator outside of the class A definition. ... How to be?

this answer illustrates how it could be.

Like molbdnilo, I also agree that Steve Jessop's answer is the best, and is the answer you should adopt for this problem.

template <class T> class A;
template <class T>
typename A<T>::B operator + (typename A<T>::B &x, typename A<T>::B &y);

template <class T>
class A {
    template <class U>
    friend typename A<U>::B operator + (typename A<U>::B &x,
                                        typename A<U>::B &y);
    struct B {
         T a,b;
         B(T x, T y) : a(x), b(y) {}
    };
    static T funcA (B & x, B & y) {
        return ::operator+<T>(x, y).a;    
    }
public:
    A () {
        B a(0, 1);
        B b(1, 0);
        funcA(a, b);
    }
};

template <class T>
typename A<T>::B operator + (typename A<T>::B &x,
                             typename A<T>::B &y) {
    return typename A<T>::B(x.a + y.a, x.b + y.b);
}
jxh
  • 69,070
  • 8
  • 110
  • 193
  • There are a few things in this design that are wrong. The first one is that you are opening the `A` type to that `operator+` which needs not have access to `A` at all. The second issue is that you are opening access to **all** specializations of `operator+` --which is wrong and prone to abuse see [this](http://stackoverflow.com/a/4661372/36565). That and the fact that it is more complicated than better solutions makes this a no-go. – David Rodríguez - dribeas Jun 25 '12 at 17:17
  • @DavidRodríguez-dribeas: I don't disagree, but I disagree with the sentiment "you can't forward declare". The correct sentiment is "you may not want to". But the answer is illustrative for the cases when someone does want to. I'll clarify the post. Thanks and regards – jxh Jun 25 '12 at 17:30
  • Note that you can still not forward declare the `operator+` as a free function, only as a *template* free function. There are differences that most people don't quite grasp, but those are different concepts. One of the most important differences in this case is that with Steve's answer the template is providing the *only* implementation available for addition, while in your proposed solution, users can change the semantics of `operator+` by providing specializations. There are other differences, but that is the most visible one. – David Rodríguez - dribeas Jun 25 '12 at 18:34
  • @DavidRodríguez-dribeas: Yes, they can specialize `operator+`, just as they could have specialized `A` itself in all the solutions. You can't stop programmers from circumventing an API when they are provided the full source in a header file except by enforcing programmer discipline. – jxh Jun 25 '12 at 19:50
  • @user315052: Sorry for santiments, but you're able to force me to respect C++. I thought I've found a dead lock of C++. But now I see I've found a dead lock of my skill :) Thanks! – OlegG Jun 26 '12 at 06:55
  • @Oleg: I thought it was a very interesting question, so thank you for that. Regards – jxh Jun 26 '12 at 07:49