Search code examples
c++friendfunction-templatesnon-member-functions

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?


Solution

  • 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.