Given the following (stripped down and contrived to highlight the problem):
#include <utility>
namespace Generic
{
class Whatever
{
public:
// ISSUE1 (member "swap()" has same name as non-member template)
void swap()
{
}
};
// Forward declaration of class template "Test"
template <class>
class Test;
// Prototype of function template "swap()" for class template "Test"
template<class T>
void swap(Test<T> &, Test<T> &);
/////////////////////////////////////////////////////////////////
// Class template "Test"
/////////////////////////////////////////////////////////////////
template <class T>
class Test : public T
{
public:
// Default constructor
Test() = default;
// Move constructor
Test(Test &&test)
: BaseClass(std::move(test))
{
}
// Assignment operator
Test& operator=(Test test)
{
// Note that the "Generic:" prefix here has no impact on the results
Generic::swap(*this, test);
return (*this);
}
private:
using BaseClass = T;
// ISSUE2 (qualified call - prefixing with "Generic:")
friend void Generic::swap<T>(Test &, Test &);
};
/////////////////////////////////////////////////////////////////
// Canonical swap function for class "Test" just above
/////////////////////////////////////////////////////////////////
template<class T>
void swap(Test<T> &, Test<T> &)
{
}
} // namespace Generic
int main()
{
using namespace Generic;
Test<Whatever> test1;
Test<Whatever> test2;
test2 = std::move(test1);
return 0;
}
Can anyone see any reason why it shouldn't compile. Note in particular ISSUE1 and ISSUE2 in the comments. These affect the results. There are 4 combinations of ISSUE1 and ISSUE2 to try and get things working (see table below), though I am aware of another way to correct things but these 4 combinations are what I want to focus on. Note that all 4 combinations compile cleanly in GCC and Clang, but only item 4 compiles cleanly in Visual Studio 2017 (latest version). The other combinations (1 to 3) fail with the errors seen in the RESULT column below (the compiler cites a problem with the ISSUE2 line in all 3 cases):
ISSUE1 ISSUE2 RESULT
------ ------ ------
1) Leave unchanged Leave unchanged 'Generic::Test': use of class template requires template argument list
'Generic::swap': not a function
2) Leave unchanged Remove "Generic:" qualifier syntax error: missing ';' before '<'
'swap': illegal use of type 'void'
'Generic::swap': 'friend' not permitted on data declarations
'Generic::swap': redefinition; previous definition was 'function'
unexpected token(s) preceding ';'
3) Comment out member "swap()" Leave unchanged 'Generic::Test': use of class template requires template argument list
'Generic::swap': not a function
4) Comment out member "swap()" Remove "Generic:" qualifier Works!!!
Is Visual Studio just buggy or do GCC and Clang have it wrong (the former appears to be the case based on my understanding of the qualified/unqualified name lookup rules, ADL, etc.). Thanks.