GCC vs Clang: Can You Overload Ref-Qualified and Non-Ref-Qualified Member Functions in C++?
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.