Search code examples
c++templatesvisual-c++language-lawyercompiler-bug

Why can't I specialize a template with an alias template in the return type in MSVC?


MSVC refuses to compile this:

#include <type_traits>

struct G { void operator()() const { } };

template<class T>
using MyTrait = std::is_void<T>;

struct S
{
    template<class F>
    std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value> run(F &&);
};
template<>
void S::run(G &&);

with the error:

<source>(14): error C2910: 'S::run': cannot be explicitly specialized
<source>(16): error C2760: syntax error: 'int' was unexpected here; expected ';'

But Clang and GCC compile it just fine. And they all compile it fine if I say std::is_void instead of MyTrait.

Why is this? Is it a compiler bug, or is there something in the language that causes this problem?


Solution

  • This is a compiler bug. Defining explicit specializations of member function templates is perfectly legal.

    std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value> is ultimately an alias for void with SFINAE, and the use of aliases shouldn't prevent specialization.

    In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done for explicit instantiations, explicit specializations, and certain friend declarations.

    - [temp.deduc.decl] p1

    The process to determine whether the full specialization void S::run(G&&) matches the member function template is the same as if you had called S::run(g) where g is an argument of type G.

    During this process, std::enable_if_t<MyTrait<decltype(std::declval<F>()())>::value> where F = G would turn into void.

    It's worth noting that your example does compile with:

    template<class F>
    std::enable_if_t<MyTrait<decltype(G{}())>::value> run(F&&);
    

    ... which is pretty much the same thing, and MSVC is being inconsistent here.

    Note on MSVC versions

    MSVC 19.35 compiles this, but more recent versions raise an error. I was unable to find a bug report for this issue, perhaps because it is relatively recent and no one has noticed/reported it yet.

    Bug report

    I've submitted a bug report for this issue.