Title
Allow implicit pointer-to-member conversion on nontype template argument
Status
nad
Section
13.4 [temp.arg]
Submitter
David Abrahams

Created on 2003-11-13.00:00:00 last changed 17 months ago

Messages

Date: 2023-06-15.19:09:35

CWG 2023-06-15

CWG noted that the derived-to-base conversion is also not allowed for arguments for non-type template parameters. Permitting such conversions is an extension, not a defect.

Date: 2023-02-15.00:00:00

Additional notes (February, 2023)

Even the following simple case fails on all major implementations:

  struct B { void f(); };
  struct D : B {};

  template<void (D::*)()>
  int g();

  int x = g<&D::f>();

Subclause 7.7 [expr.const] paragraph 12 is unambiguous that the code is ill-formed, but that seems unfortunate.

Date: 2003-11-13.00:00:00

None of my compilers accept this, which surprised me a little. Is the base-to-derived member function conversion considered to be a runtime-only thing?

  template <class D>
  struct B
  {
      template <class X> void f(X) {}
      template <class X, void (D::*)(X) = &B<D>::f<X> >
      struct row {};
  };
  struct D : B<D>
  {
      void g(int);
      row<int,&D::g> r1;
      row<char*> r2;
  };

John Spicer: This is not among the permitted conversions listed in 14.3.

I'm not sure there is a terribly good reason for that. Some of the template argument rules for external entities were made conservatively because of concerns about issues of mangling template argument names.

David Abrahams: I'd really like to see that restriction loosened. It is a serious inconvenience because there appears to be no way to supply a usable default in this case. Zero would be an OK default if I could use the function pointer's equality to zero as a compile-time switch to choose an empty function implementation:

  template <bool x> struct tag {};

  template <class D>
  struct B
  {
      template <class X> void f(X) {}

      template <class X, void (D::*pmf)(X) = 0 >
      struct row {
          void h() { h(tag<(pmf == 0)>(), pmf); }
          void h(tag<1>, ...) {}
          void h(tag<0>, void (D::*q)(X)) { /*something*/}
      };
  };

  struct D : B<D>
  {
      void g(int);
      row<int,&D::g> r1;
      row<char*> r2;
  };

But there appears to be no way to get that effect either. The result is that you end up doing something like:

      template <class X, void (D::*pmf)(X) = 0 >
      struct row {
          void h() { if (pmf) /*something*/ }
      };

which invariably makes compilers warn that you're switching on a constant expression.

History
Date User Action Args
2023-06-15 19:09:35adminsetmessages: + msg7321
2023-06-15 19:09:35adminsetstatus: open -> nad
2023-02-12 17:30:17adminsetmessages: + msg7198
2003-11-13 00:00:00admincreate