Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Friend functions of a class template

I have a class template Foo<T>.

I'd like to implement a non-member function Bar that takes two Foos and returns a Foo. I want Bar to be a non-member because it will be more natural for callers to write Bar(f1, f2) than f1.Bar(f2). I also want Bar to be inline because the calculation is trivial and frequent.

template <typename T>
inline Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs) {
  ...
}

The trick is Bar needs access to Foo's private data. I'd prefer not to have accessors to the private data--there's no good reason to expose the private data to users. So I'd like to make Bar a friend of Foo.

template <typename T>
class Foo {
  ...
  private:
    T w, x, y, z;
    friend Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs);
};

Here's where I run into trouble. The compiler complains:

The inline specifier cannot be used when a friend declaration refers to a specialization of a function template.

Is this rule imposed by the standard or is it specific to MSVC++?

Here's what I've tried:

  • Make Bar a const public member function, and then to declare a non-member version that simply returns lhs.Bar(rhs). This seems the least hacky solution.

  • Remove the inline hint, knowing that the compiler is going to decide about inlining regardless of the hint. Does this then run afoul of the one-definition rule? It will still have to be defined in a header file because it's a function template.

  • Declare the member function with a dummy template type:

    template <typename T>
    class Foo {
      ...
      private:
        T w, x, y, z;
    
        // Note that this declaration doesn't actually use Dummy.  It's just there to
        // satisfy the compiler.     
        template <typename Dummy>
        friend Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs);
    };
    

I'm not entirely sure why that works, but it does satisfy the compiler.

Is there a better solution?

like image 581
Adrian McCarthy Avatar asked Mar 14 '11 19:03

Adrian McCarthy


3 Answers

If the calculation is trivial, I would write:

template <typename T>
class Foo {
  ...
  private:
    T w, x, y, z;
  public:
    friend Foo Bar(const Foo &lhs, const Foo &rhs) {
        ...
    }
};

This doesn't fall foul of the ODR - it's an inline function with external linkage (3.2/5 excludes this from the ODR subject to the definitions being identical, 7.1.2/3 says that it's inline).

However, this doesn't define a function template Bar<T>, it just defines a set of function overloads for Bar. There may be some reason, unstated in the question, that means this won't work for you because you actually need the template.

like image 108
Steve Jessop Avatar answered Sep 23 '22 06:09

Steve Jessop


I like option 1 the best:

template <typename T>
inline Foo<T> Bar(const Foo<T> &lhs, const Foo<T> &rhs) {
    return lhs.Bar(rhs);
}

template <typename T>
class Foo {
  ...
    Foo<T> Bar(const Foo<T> &other) const;
  private:
    T w, x, y, z;
};

Then the functionality is safely contained within the class, but you provide a wrapper function for convenience.

like image 35
Tim Avatar answered Sep 23 '22 06:09

Tim


Bar is a template, so it has to be a template in the friend declaration as well.

You don't necessarily have to use a dummy parameter, but could rather use

 template <typename U>
 friend Foo<U> Bar(const Foo<U> &lhs, const Foo<U> &rhs);

You cannot use T as the template parameter here, as there is already an outer T in scope.

like image 41
Bo Persson Avatar answered Sep 22 '22 06:09

Bo Persson