Cannot have friend with auto

    struct X
    {

    private:
        int value_;
    public:
        X():value_(int())
        {}
        X(int value):value_(value)
        {}
        friend
            //int operator+(X lhs, X rhs);//THIS WILL WORK  
//BUT THE ONE BELOW WON'T
        auto operator+(const X& lhs, const X& rhs)->decltype(lhs.value_ + rhs.value_);

    };


    auto operator+(const X& lhs, const X& rhs)->decltype(lhs.value_ + rhs.value_)
    {//If I change return type to "not auto" there is no problem with friendship
        return lhs.value_ + rhs.value_;
    }

    int main(int argc, char* argv[])
    {
        X a(5);
        X b(6);
        cout << a + b;
        return 0;
    }

Cannot declare a friendship with operator+ in scenario when return type is auto. Any solution for that?

Upvotes: 1

Views: 169

Answers (3)

ds27680
ds27680

Reputation: 1993

In my opinion, in your situation, using auto does not make all that much sense, you would be probably better off saying the operator returns int.

The issue here is that in order to be able to write rhs.value_ rhs must be an instance of a complete type. The type X is considered complete after the closing } of the class. So the compiler is rightfully complaining.

EDIT3:

Since it seems to me my above explanation was not complete and could lead to confusion.

struct X
{

   //code ommited for brevity...
   //at this line one has decltype(lhs.value_ + rhs.value_) where 
   //lhs and rhs are of type X however the X type is not yet complete. 
   //The compiler has to however determine the type of the friend including 
   //the return type...
   friend auto operator+(const X& lhs, const X& rhs)-> decltype(lhs.value_ + rhs.value_);
};

EDIT:

For a discussion on VS2010 relating to decltype that explains why it is the way it is in VS2010 and also explains why it is standard compilant how it is in VS2010 you can look here:

My first option would be to drop auto. However, assuming you have provided a simpler example for illustration, and in your real case auto would actually make sense you could try:

struct X
{
private:
    int value_;
public:
    X() : value_(int()) { }

    X(int value):value_(value) { }

    template <typename T>
    auto operator+(const T& rhs) -> decltype(value_ + rhs.value_)
    {
        return value_ + rhs.value_;
    }
};

int main(int argc, char* argv[])
{
    X a(5);
    X b(6);
    std::cout << a + b;

    return 0;
}

EDIT2:

For completness (and to illustrate this is not an issue with auto and friend but rather attempting to use an incomplete type consider the following code (that also achieves what you want but in a twisted not recommendable way :-) ):

#include <iostream>

struct X
{
public:
        X() : value_(int())     { }

        X(int value):value_(value) { }

        int value_;

};

struct A : public X
{
        friend auto operator+(const X& lhs, const X& rhs) -> decltype(lhs.value_ + rhs.value_);
};

auto operator+(const X& lhs, const X& rhs) -> decltype(lhs.value_ + rhs.value_)
{
        return lhs.value_ + rhs.value_;
}

int main(int argc, char* argv[])
{
        X a(5);
        X b(6);
        std::cout << a + b;

        return 0;
}

The code compiles ok in VS2010 and gcc-4.5.1

Upvotes: 0

Jan Hudec
Jan Hudec

Reputation: 76296

It looks like a compiler bug rather than anything intentional (which compiler? consider filing a bug). However, you can always do the ugly alternative described in the specification for auto with functions (of course avoiding that is motivation for the new syntax, but if it does not work...), namely:

decltype((*(X*)0).value_ + (*(X*)0).value_)

You have the same X on both sides, so you can make a typedef in X so you don't have to type out this obnoxiousness both in declaration and definition of the operator.

(Edit) For cases where you don't have the same X on both sides, you can still factor the declaration so that it's not too horrible by preparing a template (trait) with the decltype and using it to declare the operator. Something like:

template <typename X, typename Y>
struct PlusType {
    typedef decltype((*(X*)0).value_ + (*(Y*)0).value_) Type;
}

template <typename X, typename Y>
PlusType<X, Y>::Type operator+(X &x, Y &y);

Upvotes: 1

user2100815
user2100815

Reputation:

This works with g++ 4.5.1:

class X {
   ...


    friend auto operator+( const X & a, const X & b )->decltype( X::value_ + X::value_  );
};

auto operator+( const X & a, const X & b )->decltype( X::value_ + X::value_) {
    return a.value_ + b.value_;
}

Upvotes: 1

Related Questions