4

Suppose we have the following code:

template<class T> struct S;
template<class T> void operator++(S<T>);

template<class T> struct S {
    friend void operator++(S);
};

template<class T>
void operator++(S<T>) {}

int main() {
    S<int> s;
    ++s;
}

This will compile but won't link, because the friend declaration introduces a non-template operator++, that has never been defined.

This FAQ answer reads (bold is mine):

The solution is to convince the compiler while it is examining the class body proper that the operator++ function is itself a template. There are several ways to do this;

The first way is to add <> into the friend declaration, and I'm not considering it here. The second is "to define the friend function within the class body":

template<class T> struct S {
    friend void operator++(S) { }
};

The quote suggests that void operator++(S) is now a function template and not a non-template function. Is it?

Evg
  • 25,259
  • 5
  • 41
  • 83
  • _"because the friend declaration introduces a non-template"_ It should not. `[temp.friend]/1.2` says otherwise: "For a friend function declaration that is not a template declaration: [...] if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function". In your case, non non-template `operator++` is found. – YSC Nov 13 '19 at 13:11
  • @AndyG, can we say that providing a definition turns a declaration of a non-template function into a definition of a function template? – Evg Nov 13 '19 at 13:12
  • @YSC, do you suggest that the first code snippet should [link](https://godbolt.org/z/pf39s3)? – Evg Nov 13 '19 at 13:16
  • I know it doesn't, I just fail to understand why following the Standard. – YSC Nov 13 '19 at 13:18
  • At least `gcc` makes a clear statement (that agrees with the FAQ): https://godbolt.org/z/WWRB_w – Max Langhof Nov 13 '19 at 13:27

2 Answers2

3

It is not a template, because its declaration is not that of a template (even though it appears inside a template declaration itself).

[temp.friend] (emphasis mine)

1 A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or a non-template function or class. For a friend function declaration that is not a template declaration:

  • if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a specialization of a function template, otherwise,

  • if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function, otherwise,

  • if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template ([temp.deduct.decl]), otherwise,

  • the name shall be an unqualified-id that declares (or redeclares) a non-template function.

[ Example:

template<class T> class task;
template<class T> task<T>* preempt(task<T>*);

template<class T> class task {
  friend void next_time();
  friend void process(task<T>*);
  friend task<T>* preempt<T>(task<T>*);
  template<class C> friend int func(C);

  friend class task<int>;
  template<class P> friend class frd;
};

Here, each specialization of the task class template has the function next_­time as a friend; because process does not have explicit template-arguments, each specialization of the task class template has an appropriately typed function process as a friend, and this friend is not a function template specialization; because the friend preempt has an explicit template-argument T, each specialization of the task class template has the appropriate specialization of the function template preempt as a friend; and each specialization of the task class template has all specializations of the function template func as friends. Similarly, each specialization of the task class template has the class template specialization task<int> as a friend, and has all specializations of the class template frd as friends.  — end example ]

While examples are non-normative, the one in the quote clarifies the intent of the preceding normative text. Since the friend operator declaration is not a template declaration, the text in bold applies. It therefore declares a non-template function.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 1
    I don't understand why 1.3 doesn't apply _"if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template"_ – YSC Nov 13 '19 at 13:19
  • ho... it's an unqualified id :/ Well `friend void ::operator++(S);` doesn't link either. – YSC Nov 13 '19 at 13:20
  • @YSC I'm not even sure if `friend void ::operator++(S)` should compile. The standard clearly says that it either has to match a prior declaration or be unqualified. – Max Langhof Nov 13 '19 at 13:37
  • @YSC - Rethinking, I suspect it doesn't link because only the declaration of the specialization is instantiated when deducing what `::operator++` refers to. I can't tell if it's a bug though. Unsure. – StoryTeller - Unslander Monica Nov 13 '19 at 13:44
  • @YSC - To contrast, Clang does seem to implement 1.3 and links successfully https://godbolt.org/z/EpA9dr . So... GCC bug maybe? – StoryTeller - Unslander Monica Nov 13 '19 at 13:52
  • Those narrow subtleties in Standard parsing are above my competency to be honest. – YSC Nov 13 '19 at 14:35
1

With

template<class T> struct S {
    friend void operator++(S s) { }
};

operator ++ is no longer template.

For more regular function (operator usage is a little different than function), it might allow deduction:

template<class T> struct S {
    S(T t);
    friend void foo(S lhs, S rhs) { }
};

template <typename T>
void bar(S<T> s, T t)
{
    foo(s, t);   // would not work if foo was template, would require foo<T>(s, t);
    foo(s, {t}); // would not work if foo was template, would require foo<T>(s, {t});
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302