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

2024-11-11


2808. Explicit specialization of defaulted special member function

Section: 13.9.2  [temp.inst]     Status: review     Submitter: Richard Smith     Date: 2023-09-21

Consider:

  template<typename T> struct S {
    S(const S&) = default;
    S& operator=(const S&) = default;
    int n;
  };

  // Are the explicit specializations OK?
  template<> S<int>::S(const S&) noexcept { }
  template<> S<int>& S<int>::operator=(const S& other) noexcept {
    return *this;
  }

If the explicit specialization were allowed, would the answer to std::is_trivially_copyable<S<int>> change? What is decltype(&S::operator=) (the defaulted definition is noexcept, yet no instantiation or implicit definition is triggered)?

Proposed resolution (2023-10-20) [SUPERSEDED]:

Change in 13.9.2 [temp.inst] paragraph 3 and add bullets as follows:

The implicit instantiation of a class template specialization causes The implicit instantiation of a class template specialization does not cause the implicit instantiation of default arguments or noexcept-specifiers of the class member functions.

Additional notes (October, 2023)

It is desirable to require a diagnostic for such attempted explicit specializations.

Furthermore, there are situations where the "potentially-throwing" property of a non-deleted function is queried:

  template<int...>
  struct C {
    // This class has no eligible copy assignment operator at all.
    void operator=(const C&) requires false;
    void operator=(int) noexcept;
    operator int() const;
  };

  void f(C<> &c) {
    c = c;        // Convert to int, then construct from int.
  }

  struct D {
    C<> c;
  };

  bool g(D d) {
    return noexcept(d = d);     // #1. If this is valid, what does it return?
  }

  struct E {
    C<> c;
    E &operator=(const E &o) { c = o.c; }
  };

All major implementations agree that E is valid. However, clang, gcc, and EDG delete the copy assignment operator of D, thus sidestepping the question at #1. (MSVC accepts, but #1 returns true despite the potentially-throwing conversion to int.) Apparently, user-defined conversions for the first argument of C's assignment operator are ignored in implementations other than MSVC. However, the specification is silent on that.

Possible resolution:

  1. Change in 13.9.2 [temp.inst] paragraph 3 and add bullets as follows:

    The implicit instantiation of a class template specialization causes
    • the implicit instantiation of the declarations, but not of the definitions, of the non-deleted user-provided class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; and
    • the implicit instantiation of the definitions of [ Note: The implicit instantiation determines whether a defaulted function is deleted, but a non-deleted defaulted function is implicitly defined only when it is odr-used or needed for constant evaluation. -- end note ]
    The implicit instantiation of a class template specialization does not cause the implicit instantiation of default arguments or noexcept-specifiers of the class member functions.
  2. Change in 13.9.4 [temp.expl.spec] paragraph 7 as follows:

    If a template, a member template or a member of a templated class template is explicitly specialized, a declaration of that specialization shall be reachable from every use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required unless the specialization is for a member of a templated class whose definition is implicitly instantiated as a result of the implicit instantiation of the class (13.9.2 [temp.inst]). ...