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


1715. Access and inherited constructor templates

Section: _N4527_.12.9  [class.inhctor]     Status: CD4     Submitter: Daveed Vandevoorde     Date: 2013-07-16

[Adopted at the October, 2015 meeting as P0136R1.]

Consider the following example:

  template<class T> struct S {
  private:
    typedef int X;
    friend struct B;
  };

  struct B {
    template<class T> B(T, typename T::X);
  };

  struct D: B {
    using B::B;
  };

  S<int> s;
  B b(s, 2); // Okay, thanks to friendship.
  D d(s, 2); // Error: friendship is not inherited.

My understanding is that the construction of d fails because typename T::X expands to S<int>::X in this case, and that is not accessible from D.

However, I'm not sure that makes sense from a usability perspective. The user of D just wanted to be able to wrap class B, and the fact that friendship was granted to B to enable its constructor parameter seems like just an implementation detail that D shouldn't have to cope with.

Would it perhaps be better to suspend access checking during the instantiation of inheriting member function template declarations (not definitions), since real access problems (e.g., the selection of a private constructor) would presumably be revealed when doing the full instantiation?

Proposed resolution (February, 2014):

Change _N4527_.12.9 [class.inhctor] paragraph 4 as follows:

A constructor so declared has the same access as the corresponding constructor in X. It is deleted if the corresponding constructor in X is deleted (9.5 [dcl.fct.def] 9.5.3 [dcl.fct.def.delete]). While performing template argument substitution (13.10.3 [temp.deduct]) for constructor templates so declared, name lookup, overload resolution, and access checking are performed in the context of the corresponding constructor template of X. [Example:

   struct B {
     template<class T> B(T, typename T::Q);
   };

   class S {
     using Q = int;
     template<class T>
     friend B::B(T, typename T::Q);
   };

   struct D : B {
     using B::B;
   };

   B b(S(), 1); // OK: B::B is a friend of S
   D d(S(), 2); // OK: access control is in the context of B::B

end example] An inheriting constructor shall not be explicitly instantiated (13.9.3 [temp.explicit]) or explicitly specialized (13.9.4 [temp.expl.spec]).

Additional note (June, 2014):

This issue is being returned to "review" status in light of a suggestion for an alternative approach to the problem; see issue 1941.