This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-10-26


2174. Unclear rules for friend definitions in templates

Section: 13.7.5  [temp.friend]     Status: C++17     Submitter: Richard Smith     Date: 2015-09-17

[Adopted at the February/March, 2017 meeting.]

According to 13.7.5 [temp.friend] paragraph 4,

When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (6.3 [basic.def.odr]). The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.

This seems to imply that:

  1. Instantiating a class template that contains a friend function definition instantiates the declaration, but not the definition, of that friend function, as usual (but see below).

  2. If the function is odr-used, a definition is instantiated for each such class template specialization whose template had a definition.

  3. If that results in multiple definitions, the program is ill-formed as usual.

The intent appears to be that the instantiated friend function declarations should be treated as if they were definitions, but that's not clear from the wording. This wording is also missing similar provisions for friend function template definitions; there is implementation divergence on the treatment of such cases.

There also does not appear to be wording that says that instantiating a class template specialization results in the instantiation of friend functions declared/defined therein (the relevant wording was removed from this section by issue 329). Presumably this should be covered in 13.9.2 [temp.inst] paragraph 1, which also includes the following wording that could be reused for the friend case:

However, for the purpose of determining whether an instantiated redeclaration of a member is valid according to 11.4 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition.

Also, the reliance on odr-use to trigger friend instantiation is out of date, as there are other contexts that can require an instantiation when there is no odr-use (a constexpr function invoked within an unevaluated operand).

Proposed resolution (October, 2015) [SUPERSEDED]:

  1. Delete 13.7.5 [temp.friend] paragraph 4:

  2. When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (6.3 [basic.def.odr]). The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.
  3. Change 13.9.2 [temp.inst] paragraph 1 as follows:

  4. ...The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members, and member templates, and friends; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. However, for the purpose of determining whether an instantiated redeclaration of a member is valid according to 6.3 [basic.def.odr] and 11.4 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [Example:...
  5. Change 13.9.2 [temp.inst] paragraph 3 as follows:

  6. Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. A function whose declaration was instantiated from a friend function definition is implicitly instantiated when it is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.

Proposed resolution (November, 2016):

  1. Delete 13.7.5 [temp.friend] paragraph 4:

  2. When a function is defined in a friend function declaration in a class template, the function is instantiated when the function is odr-used (6.3 [basic.def.odr]). The same restrictions on multiple declarations and definitions that apply to non-template function declarations and definitions also apply to these implicit definitions.
  3. Change 13.9.2 [temp.inst] paragraph 1 as follows, splitting it into two paragraphs as indicated:

  4. ... [Note: Within a template declaration, a local class (11.6 [class.local]) or enumeration and the members of a local class are never considered to be entities that can be separately instantiated (this includes their default arguments, exception-specifications, and non-static data member initializers, if any). As a result, the dependent names are looked up, the semantic constraints are checked, and any templates used are instantiated as part of the instantiation of the entity within which the local class or enumeration is declared. —end note]

    The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members, and member templates, and friends; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions. However, for the purpose of determining whether an instantiated redeclaration of a member is valid according to 6.3 [basic.def.odr] and 11.4 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [Example:

      template<class T, class U>
      struct Outer {
        template<class X, class Y> struct Inner;
        template<class Y> struct Inner<T, Y>;     // #1a
        template<class Y> struct Inner<T, Y> { }; // #1b; OK: valid redeclaration of #1a
        template<class Y> struct Inner<U, Y> { }; // #2
      };
    
      Outer<int, int> outer; // error at #2
    

    Outer<int, int>::Inner<int, Y> is redeclared at #1b. (It is not defined but noted as being associated with a definition in Outer<T, U>.) #2 is also a redeclaration of #1a. It is noted as associated with a definition, so it is an invalid redeclaration of the same partial specialization.

      template<typename T> struct Friendly {
        template<typename U> friend int f(U) { return sizeof(T); }
      };
      Friendly<char> fc;
      Friendly<float> ff; // ill-formed: produces second definition of f(U)
    

    end example]

  5. Change 13.9.2 [temp.inst] paragraph 3 as follows:

    Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. A function whose declaration was instantiated from a friend function definition is implicitly instantiated when it is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.