Home > front end >  Global scope friend operator declaration when class in namespace and using templated type as return
Global scope friend operator declaration when class in namespace and using templated type as return

Time:12-23

I am struggling with friend statement for a templated operator and namespaces. Sorry if I'm a bit long but I want to give a good description of my issue.

First, some context. Forget about the namespace at present. I have a class A and a public operator that needs to access its private member:

template<typename U>
struct B { U valb; };

template<typename U>
struct C { U valc; };

template<typename U,typename V>
struct A
{ 
  private:
    U v1; V v2;

  template<typename T1,typename T2>
  friend A<T1,T2> operator * ( const B<T2>&, const C<T1>& );
};

template<typename T1,typename T2>
A<T1,T2>
operator * ( const B<T2>& b, const C<T1>& c )
{
    A<T1,T2> a;
    a.v1 = c.valc * b.valb; // dummy
    return a;
}

int main()
{
    B<float> b;
    C<int> c;
    auto a = b * c;
}

This builds fine.

Now for some reason I want to put class A in a namespace, (mostly to remove it from the public API, user code will use "sub" types, declared with a using declaration). Now the trouble begins.

I am building on this answer, that covers that topic and works fine. It explains that I need to forward declare the class, then the operator, and in the friend declaration, prefix the operator with ::.

The only difference between the situation described in that linked question and mine is the return type. In my case, it is a templated type. This seems to be the trouble (or is it?)

So I tried that (online here):

template<typename U>
struct B { U valb; };

template<typename U>
struct C { U valc; };

// forward declaration of class A
namespace ns {
template<typename U,typename V> struct A;
}

// forward declaration of operator
template<typename T1,typename T2>
ns::A<T1,T2>
operator * ( const B<T2>&, const C<T1>& );

namespace ns {
  template<typename U,typename V>
  struct A                            // class declaration
  {
    template<typename T1,typename T2>
    friend A<T1,T2> ::operator * ( const B<T2>&, const C<T1>& );

    private:
      U v1; V v2;
  };
} // namespace 

// operator definition
template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>& b, const C<T1>& c )
{
    ns::A<T1,T2> a;
    a.v1 = c.valc * b.valb; // dummy
    return a;
}

int main()
{
    B<float> b;
    C<int> c;
    auto a = b * c;
}

This fails to build with:

error: ISO C   forbids declaration of 'operator*' with no type [-fpermissive]    
   23 |         friend A<T1,T2> ::operator * ( const B<T2>&, const C<T1>& );

And if I remove the ::, then the operator is not recognized as a friend.

What am I doing wrong? How can I manage that issue?

CodePudding user response:

Unless the befriend function template is declared already, I don't think you define this function template outside the class definition: essentially, there is no way to actually name the operator. To make things a bit more interesting, the two parameter types operated on are actually in a different namespace, i.e., the operator*() really needs to be defined in a different namespace than ns. It seems this does the trick:

template<typename U> struct B { U valb; };
template<typename U> struct C { U valc; };

// declaration of ns::A to declare the operator*
namespace ns { template<typename U,typename V> struct A; }

template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>&, const C<T1>& );

namespace ns {
    template<typename U,typename V>
    struct A {
        template<typename T1,typename T2>
        friend auto ::operator * ( const B<T2>&, const C<T1>& ) -> A<T1, T2>;

    private:
        U v1; V v2;
    };
}

template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>& b, const C<T1>& c ) {
    ns::A<T1,T2> a;
    a.v1 = c.valc * b.valb; // dummy
    return a;
}

int main()
{
    B<float> b;
    C<int> c;
    auto a = b * c;
}

For a live demo see this Compiler Explorer link.

  • Related