What does the standard say about this pointer-to-member-function type template parameter? Is my code wrong, or is MSVS 16.6 buggy?

Viewed 66

Here's some code that works in GCC, Clang and MSVS (at least, those versions currently available on Compiler Explorer):

template <typename T, auto T::* MemberPtr>
struct Foo
{
    Foo(const T& e) : _e(e) {}

    void operator()() const
    {
        (_e.*MemberPtr)();
    }

private:
    const T& _e;
};

struct Bar
{
    void baz() const {}

    auto bind()
    {
        using BindingType = Foo<Bar, &Bar::baz>;
        return BindingType(*this);
    }
};

int main()
{
    Bar i;
    i.bind();
}

Starting from v16.6.1, however, MSVS rejects it:

Severity  Code   Description                                                                      Line
Error     C2973  'Foo': invalid template argument 'int'                                           23
Error     E2886  cannot deduce 'auto' template parameter type "auto T::*" from "void (Bar::*)()"  21
Error     C2440  'specialization': cannot convert from 'overloaded-function' to 'auto Bar::* '    22
Error     C3535  cannot deduce type for 'auto Bar::* ' from 'int'                                 23
Error     C2440  'specialization': cannot convert from 'int' to 'int Bar::* '                     23

The code can be "fixed" in that version by taking out the T::* qualifier for MemberPtr; so:

template <typename T, auto MemberPtr>

What does the standard say about this? Is VS v16.6.1 introducing a new regression, or is it now diagnosing code that was always subtly broken?

1 Answers

The parameter/argument combo is valid as is

[temp.param]

4 A non-type template-parameter shall have one of the following (optionally cv-qualified) types:

  • ...
  • a type that contains a placeholder type.

[temp.arg.nontype]

1 If the type of a template-parameter contains a placeholder type, the deduced parameter type is determined from the type of the template-argument by placeholder type deduction. If a deduced parameter type is not permitted for a template-parameter declaration ([temp.param]), the program is ill-formed.

Now, auto T::* is a type the contains a placeholder type. And placholder type deduction works just fine in a declaration of a variable in the form

auto Bar::* foo = &Bar::baz;

So VS v16.6.1 has no business rejecting such a non-type template parameter.

Related