Friend functions of a class template
Asked Answered
M

3

8

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?

Muckrake answered 14/3, 2011 at 19:34 Comment(0)
C
5

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.

Cockayne answered 14/3, 2011 at 19:46 Comment(1)
I hadn't thought of that, and it solves the problem nicely meeting all the constraints.Muckrake
B
2

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.

Basilisk answered 14/3, 2011 at 19:46 Comment(1)
+1. I'm liking this more and more for my specific case. I'm still curious about the general case.Muckrake
D
1

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.

Dorso answered 14/3, 2011 at 19:48 Comment(2)
I tried that, but it doesn't seem right that Foo<T> would declare a friend function for Bar<U> where U is a different type than T. Possibly there's no real harm, but it felt wrong. That's how I ended up with the dummy parameter.Muckrake
It might seem wrong, but currently (C++03) you cannot select your friends for a specific T. Your Dummy parameter can also be any type different from T.Dorso

© 2022 - 2024 — McMap. All rights reserved.