When working with C++ ref-qualifiers introduced in C++11, developers sometimes run into unexpected compiler divergences between GCC, Clang, and MSVC. A common edge case occurs when attempting to overload a member function template where one version has a ref-qualifier (like &) and another does not.

The Code Example

Consider the following minimal C++ snippet:

#include <iostream>

struct S {
    template<typename... T>
    int f(T...) & { return 1; }

    template<typename... T>
    int f(T...) { return 2; }
};

int main() {
    std::cout << S{}.f(4, 5);
}

Compiler Divergence: GCC vs. Clang and MSVC

When compiling this code, you get mixed responses across different C++ compilers:

  • GCC: Accepts the code and prints 2.
  • Clang: Rejects the program during compilation with an error stating class member cannot be redeclared.
  • MSVC: Also rejects the code with a syntax/declaration error.

What Does the C++ Standard Say?

According to the C++ Standard (specifically in section [over.load]), Clang and MSVC are standard-compliant, while GCC has a compiler bug.

The C++ specification clearly defines the rules for ref-qualified member functions:

Member function declarations with the same name, the same parameter-type-list, and the same template parameter lists, if any, cannot be overloaded if any of them, but not all, have a ref-qualifier.

In plain terms: for a given member function signature, either all overloads must have ref-qualifiers (& or &&), or none of them can. You cannot mix ref-qualified member functions with non-ref-qualified member functions that share the same parameter types.

How to Fix the Code

To differentiate between calling a member function on lvalues vs. rvalues, you should explicitly ref-qualify both member function templates using & and && respectively:

#include <iostream>

struct S {
    // Lvalue ref-qualified overload
    template<typename... T>
    int f(T...) & { return 1; }

    // Rvalue ref-qualified overload
    template<typename... T>
    int f(T...) && { return 2; }
};

int main() {
    std::cout << S{}.f(4, 5); // Invokes the rvalue overload (&&) and outputs 2
}

Summary

Although GCC permissively compiles mixed ref-qualified member function overloads, doing so violates the C++ standard. Always apply the all-or-nothing rule: if you use a ref-qualifier (& or &&) on one overload in a set, you must ref-qualify all other overloads sharing that signature.